36

作为测试,我写了这段代码:

object Ambig extends App {
  def f( x:Int    ) { println("Int"   ) }
  def f( x:String ) { println("String") }
  f( null.asInstanceOf[Int   ] )
  f( null.asInstanceOf[String] )
  f(null)
}

我期待在最后一次调用 f() 时出错,说它不明确。编译器接受了它,并产生了这个输出:

Int
String
String

现在我猜测这与 Int 不是 AnyRef 的事实有关,因此适用于 f(null) 的 f 的唯一版本是 f(x:String)。但是,如果 Int 不能为 null,那么 null.asInstanceOf[Int] 是什么意思?repl 说它是 Int 类型的:

scala> :type null.asInstanceOf[Int]
Int

但我真的不明白它是如何工作的。毕竟,如果我尝试将 String 转换为 Int,那么一切都会崩溃:

scala> "foo".asInstanceOf[Int]
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
    at scala.runtime.BoxesRunTime.unboxToInt(Unknown Source)
        ...

当然这是意料之中的——“foo”不能变成 Int。但是 null 也不能,那么为什么将 null 转换为 Int 工作呢?大概是某种形式的装箱,但是类型仍然是Int,不能为null...

我错过了什么?

4

4 回答 4

53

转换null为 an的行为Int取决于它执行的上下文。

首先,如果将 anull转换为 a Int,它实际上意味着一个装箱整数,其值为null。如果将表达式放在预期类型所在的上下文中Any(这在幕后被翻译Object,因为在 JVM 字节码中,没有办法引用原始类型和具有相同引用的引用类型),那么这个value 不会进一步转换 - 这就是println(null.asInstanceOf[Int])打印null.

但是,如果您在一个原始的上下文中使用这个相同的装箱整数值Int但是,如果您在需要原始(Javaint,它将被转换为原始类型,并且null(作为引用类型的默认值)转换为0(原始类型的默认值)。

如果泛型方法执行此转换,那么自然而然地,你会得到null回报。

但是,如果这个方法是专门的,那么它的返回类型是Int(在这种情况下是一个原始整数),所以这个null: Any值必须像以前一样转换为一个原始类型。

因此,运行:

object Test extends App {
  println(null.asInstanceOf[Int])

  def printit(x: Int) = println(x)

  printit(null.asInstanceOf[Int])

  def nullint[T] = null.asInstanceOf[T]

  println(nullint[Int])

  def nullspecint[@specialized(Int) T] = null.asInstanceOf[T]

  println(nullspecint[Int])
}

产生:

null
0
null
0
于 2012-05-25T08:09:11.703 回答
21

事情是这样的:asInstanceOf不一定有意义。这个方法的作用是告诉编译器停止制造意义,并相信你所说的。

现在,如果您想知道它为什么返回 0,那是因为asInstanceOf适用于AnyRef,而不是AnyVal。当应用于 anAnyVal时,它将使用 boxed 版本,并且 boxednull的值为 0。

于 2012-05-25T18:13:46.263 回答
4

看起来它只是自动将其转换为零:

scala> null.asInstanceOf[Int]
res0: Int = 0

当然,与 null 不同,0 可以是Int.

于 2012-05-25T05:41:19.997 回答
0

首先,我们都同意我们不能分配nullhttp://www.scala-lang.org/api/current/index.html#scala.Nullscala.Int中记录的

其次,为什么当我们这样做时println(null.asInstanceOf[Int]),它会给出null
这是因为 println 的实现。它最终调用javaString.valueOf方法,即

return (obj == null) ? "null" : obj.toString();

如果您null.asInstanceOf[Int] == null在 shell 中执行 a,它将返回 true,但它会给出相反的警告,即“使用 `==' 比较 Int 和 Null 类型的值将始终产生 false”。我认为这可能是 scala 类型擦除的问题。

做 aprintln只需要一个 scala.Any 类型,所以null.asInstanceOf[Int]实际上还没有发生转换。所以我们只需要记住,当你分配null.asInstanceOf[Int]给一个 Int 时,转换是在运行时基于 Scala 的擦除语义发生的,它会为它分配 0。

顺便说一句,您仍然可以在没有任何编译错误的情况下执行 af(null),因为 scala 正在为您进行隐式转换

 null -> java.lang.Integer -> scala.Int

但是,您会看到它在运行时爆炸。

于 2015-09-26T18:46:15.253 回答