scala - 为什么我遇到这个“java.util.NoSuchElementException:空列表的头”

时间:2015-03-18 06:29:08

标签: scala

这里我有一段scala代码

abstract class Foo[T] {
  def unit: T
  def add(x:T, y:T): T
} 
object Test extends App {
  implicit object StringFoo extends Foo[String] {
    def add(x:String, y:String): String = x concat y
    def unit: String = ""
  }

  implicit object IntFoo extends Foo[Int] {
    def add(x:Int, y:Int):Int = x+y
    def unit: Int = 0
  }

  def sum[T](xs: List[T])(implicit foo: Foo[T]):T = 
    foo.add(xs.head, sum(xs.tail))


  println(sum(List(1,2,3)))
  println(sum(List("a","b","c")))
}

成功编译后,我会输入一个" java.util.NoSuchElementException:空列表的头部"

java.util.NoSuchElementException: head of empty list
at scala.collection.immutable.Nil$.head(List.scala:337)
at scala.collection.immutable.Nil$.head(List.scala:334)
at Test$.sum(test.scala:17)
at Test$.sum(test.scala:17)
at Test$.sum(test.scala:17)
at Test$.sum(test.scala:17)
at Test$delayedInit$body.apply(test.scala:20)
at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
at scala.App$$anonfun$main$1.apply(App.scala:71)
at scala.App$$anonfun$main$1.apply(App.scala:71)
at scala.collection.immutable.List.foreach(List.scala:318)
at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32)
at scala.App$class.main(App.scala:71)
at Test$.main(test.scala:5)
at Test.main(test.scala)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at scala.tools.nsc.util.ScalaClassLoader$$anonfun$run$1.apply(ScalaClassLoader.scala:71)
at scala.tools.nsc.util.ScalaClassLoader$class.asContext(ScalaClassLoader.scala:31)
at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.asContext(ScalaClassLoader.scala:139)
at scala.tools.nsc.util.ScalaClassLoader$class.run(ScalaClassLoader.scala:71)
at scala.tools.nsc.util.ScalaClassLoader$URLClassLoader.run(ScalaClassLoader.scala:139)
at scala.tools.nsc.CommonRunner$class.run(ObjectRunner.scala:28)
at scala.tools.nsc.ObjectRunner$.run(ObjectRunner.scala:45)
at scala.tools.nsc.CommonRunner$class.runAndCatch(ObjectRunner.scala:35)
at scala.tools.nsc.ObjectRunner$.runAndCatch(ObjectRunner.scala:45)
at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:74)
at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:96)
at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:105)
at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)

但是,如果我以这种方式替换方法sum,问题就会消失:

def sum[T](xs: List[T])(implicit foo: Foo[T]: T =
    if(xs.isEmpty) foo.unit
    else foo.add(xs.head, sum(xs.tail))

我确实将sum应用于非空列表,为什么我会遇到上述问题?

2 个答案:

答案 0 :(得分:3)

您感到困惑,因为您使用非空sum调用方法List,但堆栈跟踪告诉您列表为空。但是,请仔细查看堆栈和代码。

关键是第一次调用sum时不会抛出异常,而是第四次调用:

java.util.NoSuchElementException: head of empty list
at scala.collection.immutable.Nil$.head(List.scala:337)
at scala.collection.immutable.Nil$.head(List.scala:334)
at Test$.sum(test.scala:17)
at Test$.sum(test.scala:17)
at Test$.sum(test.scala:17)
at Test$.sum(test.scala:17)
at Test$delayedInit$body.apply(test.scala:20)

您已经编写了一个递归方法,这是一种自我调用的方法 - 但它会一次又一次地调用自己,而不必停下来思考它是否需要继续。不要忘记你的List(1,2,3)相当于 1 :: 2 :: 3 :: Nil,因此list.head1list.tail将提供2 :: 3 :: Nillist.tail.head将提供2等等。

xs方法中替换sum参数一次

foo.add(1, sum(List(2,3))

希望您现在可以看到完整的方法调用看起来像

foo.add(1, foo(2, foo(3, foo(Nil.head, sum(Nil.tail)))))

调用Nil.head应该失败,完全没有你给出的异常,并且添加你指定的保护条件正是修复代码的正确方法。

答案 1 :(得分:0)

因为如果xs为Nil,sum将抛出异常。 你可以从scalaDoc看起来没有 试试这段代码

def sum[T](xs: List[T])(implicit foo: Foo[T]):T =
    xs match {
        case List() =>
            throw new IllegalArgumentException("empty list!")
        case List(x) => x
        case x :: rest => foo.add(x, sum(rest))
}