先看个小问题
先贴下一段Scala
代码,看下这段代码是否存在问题?
1 2 3 4 5
| val persons = List[Person](Person("tom"), Person("marry"), null).iterator var person: Person = null while ((person = persons.next()) != null) { println("obj name: " + person.name) }
|
如果你的答案是这段代码运行不会出任何问题
的话,那么你对于 Scala 的变量赋值还是了解太少。
为什么呢
在我们一般的认知中,在 Java 和 C++ 中对变量赋值后,其会返回相对应该变量的值,而在 Scala 中,如果对变量赋值后,获取到的返回值却统一是 Unit。
Unit 是表示为无值,其作用与其他语言中的 void 作用相同,用作不返回任何结果的方法的结果类型。
回到刚才那段代码,根据以上说明,如果我们在赋值对person
变量的话,那就会导致在每一次循环当中,其实我们一直都是拿 Unit 这个值去与 null 比较,那么就可以换做一个恒等式为Unit != null
,这样做的结果就是这个循环不会中断。
在 IDEA 中,如果我们仔细查看代码,发现 IDE 已经提醒我们这个问题的存在了,这这也仅仅只是 Warning 而已。
若通过编译的方法查看源代码的话,会在编译的过程中,获得这样一句警告(并非错误!):
有个简单的例子可以检验自己是否明白懂了这个”bug”:
1 2 3
| var a: Int = 0 var b: Int = 0 a = b = 1
|
解决方案
在给出常见的解决方案前,先给出为什么 Scala 要这样设计的理由(Scala 之父亲自解释):
https://stackoverflow.com/questions/1998724/what-is-the-motivation-for-scala-assignment-evaluating-to-unit-rather-than-the-v
常见的解决方案会有以下几种:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| var person = null while ({person = persons.next; person != null}) { println("obj name: " + person.name) }
Iterator.continually(persons.next()) .takeWhile(_ != null) .foreach(t => {println("obj name: " + t.name)})
Stream.continually(persons.next()) .takeWhile(_ != null) .foreach(t => {println("obj name: " + t.name)})
|
参考资料:
- https://stackoverflow.com/questions/6881384/why-do-i-get-a-will-always-yield-true-warning-when-translating-the-following-f
- https://stackoverflow.com/questions/3062804/scala-unit-type
- https://stackoverflow.com/questions/2442318/how-would-i-express-a-chained-assignment-in-scala