Если Int не может быть нулевым, что означает значение null.asInstanceOf [Int]?
В качестве теста я написал этот код:
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, которая работает для f (null), является f (x: String). Но тогда, если Int не может быть нулевым, что означает значение 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 в Int работает? Предположительно бокс в той или иной форме, но тип все еще Int, который не может быть нулевым...
Что мне не хватает?
Ответы
Ответ 1
Поведение отбрасывания null
к Int
зависит от контекста, в котором он выполняется.
Прежде всего, если вы добавили null
в Int
, это на самом деле означает целое число в штучной упаковке, значение которого null
. Если вы помещаете выражение в контекст, где ожидаемый тип Any
(который переводится в Object
за сценой, потому что в байт-коде JVM нет способа ссылаться на примитивный тип и ссылочный тип с то эта ссылка), то это значение больше не преобразуется - поэтому println(null.asInstanceOf[Int])
печатает null
.
Однако, если вы используете это одно целое число в квадрате в контексте, где ожидается примитив Int
(Java Int
), он будет преобразован в примитив, а 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
Ответ 2
Здесь вещь: asInstanceOf
не имеет смысла. То, что этот метод делает, это сказать компилятору STOP MAKING SENSE и доверять тому, что вы говорите.
Теперь, если вы хотите знать, почему он возвращает 0, это потому, что asInstanceOf
работает на AnyRef
, а не на AnyVal
. При применении к AnyVal
вместо этого он будет использовать версию в коробке, а в коробке null
будет значение 0.
Ответ 3
Похоже, он просто автоматически преобразует его в ноль:
scala> null.asInstanceOf[Int]
res0: Int = 0
И, конечно, 0, в отличие от нуля, может быть Int
.
Ответ 4
Во-первых, мы все согласны с тем, что мы не можем назначить null
на scala.Int
, как описано в http://www.scala-lang.org/api/current/index.html#scala.Null
Во-вторых, почему, когда мы делаем println(null.asInstanceOf[Int])
, он дает null
?
Это происходит из-за реализации println. В итоге он вызывает метод java String.valueOf
, который
return (obj == null) ? "null" : obj.toString();
Если вы выполните null.asInstanceOf[Int] == null
в оболочке, он вернет true, но дает противоположное предупреждение о том, что "сравнение значений типов Int и Null с использованием` == 'всегда будет давать false". Я думаю, что это может быть проблемой в стирании типа scala.
Выполнение println
требует только scala. Любой тип, поэтому кастинг null.asInstanceOf[Int]
на самом деле еще не произошел. Поэтому нам просто нужно помнить, что когда вы назначаете null.asInstanceOf[Int]
Int, приведение происходит во время выполнения на основе семантики стирания scala и присваивает ему 0.
Кстати, вы все равно можете сделать f (null) без какой-либо ошибки компиляции, потому что scala делает неявное преобразование для вас
null -> java.lang.Integer -> scala.Int
Однако вы увидите, что он взрывается во время выполнения.