Java訪問Scala中的Int類型

出錯代碼

寫java 和 scala 混合代碼的時候遇到一個小問題

def extractRefInputFieldsWithType(exprs: JList[RexNode]): Array[(Int, RelDataType)] = {
  val visitor = new InputRefVisitor
  // extract referenced input fields from expressions
  exprs.foreach(_.accept(visitor))
  visitor.getFieldsWithType
}
final scala.Tuple2<Integer, RelDataType>[] refFields =
      RexNodeExtractor.extractRefInputFieldsWithType(project.getProjects());

image.png
IDE提示的錯誤是返回的類型是 Tuple2<Object, RelDataType> 但是我們承接的類型是Tuple2<Integer, RelDataType>

原因

這本質原因是因為scala中的Int和java的Integer並不對標。
從這個 api介紹中//www.scala-lang.org/api/current/scala/Int.html我們可以知道scala中Int是一個value class (繼承自 AnyVal) 有點類似java中的新提案中的 value types,可以讓用戶定義的類型在運行時不需要裝箱拆箱操作,可以減少不必要的堆內存分配。
從Stack Overflow上看到這樣的測試樣例

class SomeClass {
  def testIntTuple: (Int, Int) = (0, 1)
  def testIntegerTuple: (java.lang.Integer, java.lang.Integer) = (0, 1)
  def testIntArray: Array[Int] = Array(1, 2)
}
javap SomeClass 
Compiled from "IntValue.scala" 
public class org.apache.flink.table.planner.plan.stream.sql.SomeClass {
  public scala.Tuple2<java.lang.Object, java.lang.Object> testIntTuple();
  public scala.Tuple2<java.lang.Integer, java.lang.Integer> testIntegerTuple();
  public int[] testIntArray();
  public org.apache.flink.table.planner.plan.stream.sql.SomeClass();
}

通過反編譯之後的代碼可以看到運行時表示的類型是Object類型,而如果直接返回的類型是Array[Int] 則相應的表示的類型是int[]
image.png
從上面這段描述可以看到,因為Int類型是value class 所以在運行時並不直接對應到java.lang.Integer 因為scala中實現了value class的語義,所以他不需要將其轉化成包裝類,這樣就可以獲得更好的性能,避免創建Int值時還需要堆上分配內存和創建引用。
因此轉到java class時/或者java的泛型參數時就沒有直接的Reference類型映射,而轉到數組時,就可以直接表示為 primitive 數組int[]

參考

//stackoverflow.com/questions/10248180/scala-tuple-type-inference-in-java
//scala.cool/2017/07/scala-types-of-types-part-4/#17-value-%E7%B1%BB
//docs.scala-lang.org/overviews/core/value-classes.html value class 介紹
//www.jesperdj.com/2015/10/04/project-valhalla-value-types/ java value types
//openjdk.org/projects/valhalla/ jep