将类型化集合从 Scala 2.13 方法返回给 Java 8 调用者
Returning typed collection from Scala 2.13 method to Java 8 caller
我想 return java.util.List<Integer>
s 到下面的 Java 8 代码而不是 return 从以下 Scala 编辑的 java.util.List<Object>
s 2.13.0 代码。 Java 和 Scala 代码都可以修改以适应。我不想强制进行任何类型转换,例如没有 (java.util.List<Integer>)
或 .asInstanceOf[java.util.List[Int]]
转换。
collections.FunctionConverterFromJava.scala
trait FunctionConverterFromScala {
import java.util.{List => JList}
import scala.collection._
val intoEvenOdd: JList[Int] => (JList[Int], JList[Int]) = {
import scala.jdk.CollectionConverters._
javaList: JList[Int] =>
javaList.asScala.partition(_ % 2 == 0) match {
case (even: mutable.Buffer[Int], odd: mutable.Buffer[Int]) =>
(even.asJava, odd.asJava)
}
}
}
object FunctionConverterFromJava extends FunctionConverterFromScala {
import java.util.{function, List => JList}
def reverse(string: String): String = string.reverse
def zipChars(string: String): IndexedSeq[(Char, Int)] = string.zipWithIndex
val intoEvenOddForJava: function.Function[JList[Int], (JList[Int], JList[Int])] = {
import scala.jdk.FunctionConverters._
intoEvenOdd.asJava
}
}
object FunctionConverterFun extends App with FunctionConverterFromScala {
val jList: java.util.List[Int] = {
import scala.jdk.CollectionConverters._
(1 to 10).asJava
}
println(intoEvenOdd(jList))
}
Java 程序
import collections.FunctionConverterFromJava$;
import scala.Tuple2;
import scala.collection.immutable.IndexedSeq;
import java.util.Arrays;
import java.util.List;
public class FunctionConverterFun {
public static void main(String[] args) {
String string = "Hello!";
String reversed = FunctionConverterFromJava$.MODULE$.reverse(string);
System.out.println("reversed = " + reversed);
IndexedSeq<Tuple2<Object, Object>> zippedChars = FunctionConverterFromJava$.MODULE$.zipChars(string);
System.out.println("zippedChars = " + zippedChars);
List<Object> list1 = Arrays.asList(1, 2);
Tuple2<List<Object>, List<Object>> list2 = FunctionConverterFromJava$.MODULE$.intoEvenOddForJava().apply(list1);
System.out.println("list2 = " + list2);
java.util.function.Function<List<Object>, Tuple2<List<Object>, List<Object>>> f = FunctionConverterFromJava$.MODULE$.intoEvenOddForJava();
Tuple2<List<Object>, List<Object>> list3 = f.apply(list1);
System.out.println("list3 = " + list3);
}
}
问题是 type erasure 将 java.util.List[scala.Int]
变成了 java.util.List<java.lang.Object>
。例如,intoEvenOddForJava
的 javap
输出是
public scala.Function1<java.util.List<java.lang.Object>, scala.Tuple2<java.util.List<java.lang.Object>, java.util.List<java.lang.Object>>> intoEvenOddForJava();
但是,如果我们像这样从 scala.Int
更改为 java.lang.Integer
object FunctionConverterFromJava extends FunctionConverterFromScala {
...
val intoEvenOddForJava: function.Function[JList[java.lang.Integer], (JList[java.lang.Integer], JList[java.lang.Integer])] = {
intoEvenOdd.asJava.asInstanceOf[function.Function[JList[java.lang.Integer], (JList[java.lang.Integer], JList[java.lang.Integer])]]
}
}
然后我们根据javap
输出
避免类型擦除
public java.util.function.Function<java.util.List<java.lang.Integer>, scala.Tuple2<java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>>> intoEvenOddForJava();
现在从Java开始我们可以按需调用
java.util.function.Function<List<Integer>, Tuple2<List<Integer>, List<Integer>>> f = FunctionConverterFromJava$.MODULE$.intoEvenOddForJava();
Tuple2<List<Integer>, List<Integer>> list3 = f.apply(list1);
System.out.println("list3 = " + list3);
输出
list3 = ([2],[1])
现在我明白你想避免 asInstanceOf
,但是注意它会隐藏在库 FunctionConverterFromJava
中,也就是说,call-site 会清除 asInstanceOf
.如果您仍然希望完全避免 asInstanceOf
,请考虑将以下内容添加到 FunctionConverterFromScala
val intoEvenOddAsJava: JList[java.lang.Integer] => (JList[java.lang.Integer], JList[java.lang.Integer]) = {
import scala.jdk.CollectionConverters._
javaList: JList[java.lang.Integer] =>
javaList.asScala.partition(_ % 2 == 0) match {
case (even: mutable.Buffer[java.lang.Integer], odd: mutable.Buffer[java.lang.Integer]) =>
(even.asJava, odd.asJava)
}
}
然后在FunctionConverterFromJava.intoEvenOddForJava
中调用intoEvenOddAsJava.asJava
。
藏在图书馆里的 asInstanceOf
可能是可以接受的 because
...the underlying representation of Int
is Integer
you can cast
directly to java.util.List[java.lang.Integer]
解决评论,Eugene 的回答
...how does it know
about Integer (via that checkcast
) if generics are erased? The answer
is the optional Signature
that is generated when A is compiled, or in
your cases:
()Lscala/collection/immutable/List<Ljava/lang/Object;>; //fooInt
()Lscala/collection/immutable/List<Ljava/lang/Integer;>; // fooInteger
This Signature
information is what is used by the compiler to enforce
type safety at callsites, via runtime checks; if this field would not
be present - that would have been impossible.
我想 return java.util.List<Integer>
s 到下面的 Java 8 代码而不是 return 从以下 Scala 编辑的 java.util.List<Object>
s 2.13.0 代码。 Java 和 Scala 代码都可以修改以适应。我不想强制进行任何类型转换,例如没有 (java.util.List<Integer>)
或 .asInstanceOf[java.util.List[Int]]
转换。
collections.FunctionConverterFromJava.scala
trait FunctionConverterFromScala {
import java.util.{List => JList}
import scala.collection._
val intoEvenOdd: JList[Int] => (JList[Int], JList[Int]) = {
import scala.jdk.CollectionConverters._
javaList: JList[Int] =>
javaList.asScala.partition(_ % 2 == 0) match {
case (even: mutable.Buffer[Int], odd: mutable.Buffer[Int]) =>
(even.asJava, odd.asJava)
}
}
}
object FunctionConverterFromJava extends FunctionConverterFromScala {
import java.util.{function, List => JList}
def reverse(string: String): String = string.reverse
def zipChars(string: String): IndexedSeq[(Char, Int)] = string.zipWithIndex
val intoEvenOddForJava: function.Function[JList[Int], (JList[Int], JList[Int])] = {
import scala.jdk.FunctionConverters._
intoEvenOdd.asJava
}
}
object FunctionConverterFun extends App with FunctionConverterFromScala {
val jList: java.util.List[Int] = {
import scala.jdk.CollectionConverters._
(1 to 10).asJava
}
println(intoEvenOdd(jList))
}
Java 程序
import collections.FunctionConverterFromJava$;
import scala.Tuple2;
import scala.collection.immutable.IndexedSeq;
import java.util.Arrays;
import java.util.List;
public class FunctionConverterFun {
public static void main(String[] args) {
String string = "Hello!";
String reversed = FunctionConverterFromJava$.MODULE$.reverse(string);
System.out.println("reversed = " + reversed);
IndexedSeq<Tuple2<Object, Object>> zippedChars = FunctionConverterFromJava$.MODULE$.zipChars(string);
System.out.println("zippedChars = " + zippedChars);
List<Object> list1 = Arrays.asList(1, 2);
Tuple2<List<Object>, List<Object>> list2 = FunctionConverterFromJava$.MODULE$.intoEvenOddForJava().apply(list1);
System.out.println("list2 = " + list2);
java.util.function.Function<List<Object>, Tuple2<List<Object>, List<Object>>> f = FunctionConverterFromJava$.MODULE$.intoEvenOddForJava();
Tuple2<List<Object>, List<Object>> list3 = f.apply(list1);
System.out.println("list3 = " + list3);
}
}
问题是 type erasure 将 java.util.List[scala.Int]
变成了 java.util.List<java.lang.Object>
。例如,intoEvenOddForJava
的 javap
输出是
public scala.Function1<java.util.List<java.lang.Object>, scala.Tuple2<java.util.List<java.lang.Object>, java.util.List<java.lang.Object>>> intoEvenOddForJava();
但是,如果我们像这样从 scala.Int
更改为 java.lang.Integer
object FunctionConverterFromJava extends FunctionConverterFromScala {
...
val intoEvenOddForJava: function.Function[JList[java.lang.Integer], (JList[java.lang.Integer], JList[java.lang.Integer])] = {
intoEvenOdd.asJava.asInstanceOf[function.Function[JList[java.lang.Integer], (JList[java.lang.Integer], JList[java.lang.Integer])]]
}
}
然后我们根据javap
输出
public java.util.function.Function<java.util.List<java.lang.Integer>, scala.Tuple2<java.util.List<java.lang.Integer>, java.util.List<java.lang.Integer>>> intoEvenOddForJava();
现在从Java开始我们可以按需调用
java.util.function.Function<List<Integer>, Tuple2<List<Integer>, List<Integer>>> f = FunctionConverterFromJava$.MODULE$.intoEvenOddForJava();
Tuple2<List<Integer>, List<Integer>> list3 = f.apply(list1);
System.out.println("list3 = " + list3);
输出
list3 = ([2],[1])
现在我明白你想避免 asInstanceOf
,但是注意它会隐藏在库 FunctionConverterFromJava
中,也就是说,call-site 会清除 asInstanceOf
.如果您仍然希望完全避免 asInstanceOf
,请考虑将以下内容添加到 FunctionConverterFromScala
val intoEvenOddAsJava: JList[java.lang.Integer] => (JList[java.lang.Integer], JList[java.lang.Integer]) = {
import scala.jdk.CollectionConverters._
javaList: JList[java.lang.Integer] =>
javaList.asScala.partition(_ % 2 == 0) match {
case (even: mutable.Buffer[java.lang.Integer], odd: mutable.Buffer[java.lang.Integer]) =>
(even.asJava, odd.asJava)
}
}
然后在FunctionConverterFromJava.intoEvenOddForJava
中调用intoEvenOddAsJava.asJava
。
藏在图书馆里的 asInstanceOf
可能是可以接受的 because
...the underlying representation of
Int
isInteger
you can cast directly tojava.util.List[java.lang.Integer]
解决评论,Eugene 的回答
...how does it know about Integer (via that
checkcast
) if generics are erased? The answer is the optionalSignature
that is generated when A is compiled, or in your cases:
()Lscala/collection/immutable/List<Ljava/lang/Object;>; //fooInt
()Lscala/collection/immutable/List<Ljava/lang/Integer;>; // fooInteger
This
Signature
information is what is used by the compiler to enforce type safety at callsites, via runtime checks; if this field would not be present - that would have been impossible.