首页 > 解决方案 > Scala 中的哪个函数将 java.lang.Integer (null) 转换为 scala Int (zero)?

问题描述

最近发现了一个意外行为(感谢单元测试)。

斯卡拉:2.13.[4,6]

代码示例:

scala> def pri(value: Int): Int = value
def pri(value: Int): Int

scala>val jInt: java.lang.Integer = null
val jInt: Integer = null

scala> pri(jInt)
val res0: Int = 0

我希望NullPointerException这里有一个,但显然 JavaInteger null被隐式转换Int为零。

Scala的Predef.scala.Integer2int似乎没有被称为:

implicit def Integer2int(x: java.lang.Integer): Int         = x.intValue

标签: scala

解决方案


在 Scala 中,Int类型对应于 java int

Scala 类型系统分为AnyValAnyRef层次结构,它们都是Any.

AnyVal层次结构具有对应于 Java 原始类型的 等IntLong这些AnyVals 可以boxed转换为相应的AnyRef类型(即Integerfor Int)。


Int从to Integer(称为boxing) 的类型转换是通过Int.box调用发生的,这个实现来自运行时实现scala.runtime.BoxesRunTime.boxToInteger,它被定义为,

public static java.lang.Integer boxToInteger(int i) {
    return java.lang.Integer.valueOf(i);
}

因此,这首先适应i: Inta int i,然后转换为Integerusing java.lang.Integer.valueOf(i)

类似地,从Integerto Int(称为unboxing)的类型转换是由 完成的Int.unbox。这个实现来自运行时实现scala.runtime.BoxesRunTime.unboxToInt,它被定义为,

public static int unboxToInt(Object i) {
    return i == null ? 0 : ((java.lang.Integer)i).intValue();
}

请注意null签入null ? 0 : ((java.lang.Integer)i).intValue()。这unboxing对“增加空值安全性”有一点“改进”,但可以throwaClassCastException作为输入可以是 any Object


Scala Predef 有以下两个在implicits需要implicitly时进行这种类型转换。

implicit def int2Integer(x: Int): java.lang.Integer = x.asInstanceOf[java.lang.Integer]

implicit def Integer2int(x: java.lang.Integer): Int = x.asInstanceOf[Int]

请注意,从Scala 2.12.0这些方法开始进行类型转换,并且不要调用任何方法,例如x.intValue(这是完成Scala 2.11.x的,您将看到与NullPointerException在 Java 中看到的相同,但随后它与显式Int.box调用不一致,这可能是原因对于implicit definitions) 的变化。


Scala-Java 互操作有很多“意想不到的”情况,因此当您将 Scala 与几乎所有 Java 库一起使用时,您需要非常小心(有趣的是,我们几乎所有的东西都使用 Java 库)。

这里有一些惊喜int,IntInteger

public class JavaIntMagic {

    public static int toPrimitiveInt(Integer integer) {
        return integer;
    }

    public static Integer toInteger(int i) {
        return i;
    }

}
object ScalaIntMagic {
  def toPrimitiveInt(integer: java.lang.Integer): Int = integer

  def toInteger(i: Int): java.lang.Integer = i
}

当从 Scala 和 Java 代码调用时,这些实现具有不同的行为。

public class JavaMain {

    public static void main(String[] args) {
        Integer integer = null;

        int i1 = ScalaIntMagic.toPrimitiveInt(integer);
        // 0

        int i2 = JavaIntMagic.toPrimitiveInt(integer);
        // fails with NullPointerException

        Integer integer1 = ScalaIntMagic.toInteger(integer);
        // fails with NullPointerException

        Integer integer2 = JavaIntMagic.toInteger(integer);
        // fails with NullPointerException
    }

}

object ScalaMain {

  def main(args: Array[String]): Unit = {
    val integer: java.lang.Integer = null

    val i1: Int = ScalaIntMagic.toPrimitiveInt(integer)
    // 0

    val i2: Int = JavaIntMagic.toPrimitiveInt(integer)
    // fails with NullPointerException

    val integer1: java.lang.Integer = ScalaIntMagic.toInteger(integer)
    // 0
    // why ? because the "null safe" unboxing strikes again

    val integer2: java.lang.Integer = JavaIntMagic.toInteger(integer)
    // 0
    // why ? because the "null safe" unboxing strikes again

  }

}

在使用或其他 java 驱动程序时,当您的数据库表神奇地有一些数字而不是预期的(或写入失败)JDBC时,请不要感到惊讶。添加越来越多的单元测试(即使是看起来很微不足道的东西)。0null

奇怪的是,一方面,我们没有任何用于网络、数据库等基础内容的“纯”Scala 库,并且严重依赖包装在 Scala API 中的 Java 库,但另一方面,我们没有为了在 Scala 生态系统中可靠地使用这些库,不强调一致和可预测的 Java 互操作(即使是非常基本的东西,如 int、boolean、bytes)。


推荐阅读