【问题标题】:Returning typed collection from Scala 2.13 method to Java 8 caller将类型化集合从 Scala 2.13 方法返回给 Java 8 调用者
【发布时间】:2019-07-17 19:30:33
【问题描述】:

我想将 java.util.List<Integer>s 返回到下面的 Java 8 代码,而不是从以下 Scala 2.13.0 代码返回的 java.util.List<Object>s。 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);
    }
}

【问题讨论】:

    标签: java scala function scala-java-interop


    【解决方案1】:

    问题是type erasurejava.util.List[scala.Int] 变成java.util.List&lt;java.lang.Object&gt;。例如,intoEvenOddForJavajavap 输出是

    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 中,也就是说,调用站点将不包含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

    ...Int 的底层表示是 Integer 你可以转换 直接到java.util.List[java.lang.Integer]


    解决评论,尤金的回答explains

    ...它怎么知道 如果泛型被删除,关于整数(通过checkcast)?答案 是编译 A 时生成的可选 Signature,或者在 你的案例:

    ()Lscala/collection/immutable/List&lt;Ljava/lang/Object;&gt;; //fooInt ()Lscala/collection/immutable/List&lt;Ljava/lang/Integer;&gt;; // fooInteger

    这个Signature 信息是编译器用来强制执行的 通过运行时检查在调用点进行类型安全;如果这个字段不会 在场——那是不可能的。

    【讨论】:

    • 是的,这行得通。 建议:添加import java.lang.{Integer =&gt; JInt} 问题:为什么JList[JInt]没有遇到与JList[Int]相同的问题?
    • 感谢您尝试改进您的答案。是的,以某种方式涉及类型擦除,但是您提供的答案级别本质上是“因为类型擦除”,我想要一个更具体的答案。如果集合具有用户定义的类型(扩展 AnyValAnyRef),我预计问题会返回 Object,除非更好地理解产生不同的咒语。
    • @MikeSlinn 请参阅编辑后的答案,其中包含 Eugene 广泛回答的链接。
    猜你喜欢
    • 2011-06-06
    • 2021-07-25
    • 1970-01-01
    • 1970-01-01
    • 2015-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-17
    相关资源
    最近更新 更多