3

为什么这里的默认值在显式分配给 val 时表现不同,而不是直接打印?

package blevins.example

class SimpleWrap[T] {
  var t: T = _
  def get = t
}

object App extends Application {
  val swb = new SimpleWrap[Boolean]
  val b = swb.get
  println("b: " + b)  // b: false
  println("swb.get: " + swb.get) // swb.get: null

  val swi = new SimpleWrap[Int]
  val i = swi.get
  println("i: " + i) // i: 0
  println("swi.get: " + swi.get) // swi.get: null
}

我正在使用 2.8r19890。


编辑- 当“get”被称为期待一个 Any 时,似乎会发生奇怪的事情。

  val any1: Any = swb.get
  val any2: Any = b
  println("any1: " + any1) // any1: null
  println("any2: " + any2) // any2: false
4

1 回答 1

4

我很确定这与原语的装箱/拆箱有关。如果您编写通用代码来处理原语,则必须将原语装箱,然后在您将其用作原语的地方将其拆箱。我不确定使用什么拆箱算法,但我想它是沿着以下几行:

if(box == null) 
  default value
else
  box.unbox

因此,我可能会非常奇怪地补充一下,t简单包装类中字段的默认值始终是null,因为该字段始终是盒装原语,因为泛型是通过类型擦除在 JVM 级别实现的。因此,JVM 看到的只是t类型Object,值为null。因此,该方法get将始终 return null,但是当泛型方法get应该返回原始类型时,null将取消装箱为默认值。

此外,稍微用反射来探索确实表明该领域确实是null.

val sw = new SimpleWrap[Boolean]
sw.getClass.getDeclaredFields.map {
  f => f.setAccessible(true)
  f.get(sw) 
  }

哦,s的乐趣null。这个问题的一个解决方案是使用 2.8@specialised注释,如果它已在您使用的每晚构建中实现。

或者,更好的是,Scala 编译器可以将这些字段默认为使用的原语的实际默认值的盒装默认值。例如,在 a 的情况下SimpleWrap[Boolean]t将在运行时具有类型Object和值java.lang.Boolean(false)

编辑:提交的错误报告。

另一个奇怪的事情:

val x: Int = null.asInstanceOf[Int] // 0
val y: Boolean = null.asInstanceOf[Boolean] // false

这是应该解决的问题,以便泛型真正通用,并具有一致的行为!目前,您的get方法没有一致的行为。

——弗拉维乌·西普西根

于 2009-12-05T23:23:42.357 回答