java - 如何从java实例化在scala中定义的嵌套泛型类?

标签 java scala inner-classes scala-java-interop

我正在尝试从 Java 实例化一个嵌套的通用 Scala 类并遇到此编译错误。有人可以帮忙吗?谢谢

外部.scala:

class Outer {
    class Inner[A]
}

sctest.java:
public class sctest{
    public static void main(String[] args) {
        Outer o = new Outer();
        Outer.Inner<String> a = o.new Inner<String>();
    }
}

$ javac sctest.java

sctest.java:4:错误:类 Outer.Inner 中的构造函数 Inner 不能应用于给定类型;
Outer.Inner a = o.new Inner();
^
要求:外
发现:没有参数
原因:实际参数列表和形式参数列表的长度不同
其中 A 是一个类型变量:
在类 Outer.Inner 中声明的扩展对象
1 个错误

最佳答案

我不知道如何从 Java 中完成此操作。详情见附录。我将直接跳到解决方法建议。

每当您在尝试从 Java 调用 Scala 代码时遇到 Java-Scala 互操作问题时,有一个简单但可能有点重量级的解决方法,它基本上适用于所有情况。

TL;博士

如果在 Java 代码中实例化 Scala 实体不起作用,请将所有内容隐藏在 Java 接口(interface)后面,并在 Scala 中实现此接口(interface)。

解决方案建议

如果你使用的是一些主流的 Scala 框架,它可能
有一个完全独立的 Java API(例如 Spark、Akka、Play),那么
请使用它!

如果它是一个鲜为人知的 Scala 软件包,没有单独的
Java API,请执行以下操作:

  • 用纯 Java 编写一个最小的、干净的面向用户的 API
  • 在 Java 中提供面向框架的接口(interface)
  • 在 Scala 中实现 Java 接口(interface)
  • 提供 Java API 实现的入口点
  • 在您的 Java 代码中使用纯 Java API

  • 如果您不想实现完整的 Java API,您可以在本地使用这种方法来处理那些无法与您的 Java 项目无缝协作的 Scala 代码部分。

    为什么它应该起作用:Scala 总是试图适应 Java。
    Java 完全忽略了 Scala。因此,它更容易
    从 Scala 调用 Java 代码,并在 Scala 中实现 Java 接口(interface)
    Scala,而不是相反。

    以下是如何将此模式应用于您的示例(我对其进行了一些扩展以使其不平凡):

    请注意,类名有些难看,我这样做只是为了省略包声明,以便所有文件都可以转储到 src/main/{scala,java};不要在实际实现中这样做。

    步骤 0:看第三方Scala库

    假设这是我们的第三方 Scala 库,
    superImportantMethod s 并计算 superImportantThings :
    /* Suppose that this is the external Scala library 
     * that you cannot change.
     * A = Outer
     * B = Inner
     * 
     * + I added at least one member and one
     * method, so that it's not so boring and trivial
     */
    
    class A {
      var superImportantMemberOfA: Int = 42
      class B[T](t: T) {
        def superImportantMethod: String = t + "" + superImportantMemberOfA
      }
    }
    

    第 1/2 步:用于 Java 项目的最小 Java API

    我们将在我们的java项目中使用这些接口(interface),并实现它们
    直接使用 Scala:
    interface A_j {
      <X> B_j<X> createB(X x);
    }
    
    interface B_j<X> {
      String superImportantMethod();
    }
    

    第 3 步:在 Scala 中实现接口(interface)
    /** Implementation of the java api in 
      * Scala
      */
    class A_javaApiImpl extends A_j {
      private val wrappedA: A = new A
    
      private class B_javaApiImpl[X](val x: X) extends B_j[X] {
        private val wrappedB: wrappedA.B[X] = new wrappedA.B[X](x)
        def superImportantMethod: String = wrappedB.superImportantMethod
      }
    
      def createB[X](x: X): B_j[X] = new B_javaApiImpl[X](x)
    }
    

    第四步:提供 API 的入口点
    /** Some kind of entry point to the 
      * java API.
      */
    object JavaApi {
      def createA: A_j = new A_javaApiImpl
    }
    

    第五步:在您的 Java 代码中使用仅限 java 的 API:
    public class JavaMain {
      public static void main(String[] args) {
    
        // Use the Java API in your Java application
        // Notice that now all A_j's and B_j's are
        // pure Java interfaces, so that nothing 
        // should go wrong.
        A_j a = JavaApi.createA(); // the only call of a Scala-method.
        B_j<String> b = a.createB("foobarbaz");
        System.out.println(b.superImportantMethod());
      }
    }
    

    现在应该不会出错,因为 Java(几乎)从不调用任何
    Scala 方法或构造函数,并且通过定义一个干净的 API,您还
    保证您不会遇到任何问题,因为
    一些 Scala 概念无法用 Java 表示。确实如此
    编译并运行:
     [info] Running (fork) JavaMain 
     [info] foobarbaz42
    

    附录一

    (失败)尝试从 Java 实例化通用内部 Scala 类

    我从这些定义开始(从您的问题中略微缩写的代码):

    .scala:
    class A {
      class B[T]
    }
    

    .java:
    public class JavaMain {
      public static void main(String[] args) {
        A a = new A();
        A.B<String> b = a.new B<String>(a);
      }
    }
    

    请注意,javac 编译器需要一个类型为 A 的参数。对于B构造函数,
    这已经有点可疑,而且不是很直观。

    它编译了,但是当我尝试运行它时,我收到了以下神秘的错误消息:

    [错误] 线程“main”中的异常 java.lang.NoSuchMethodError: A$B.(LA;LA;)V
    [错误] 在 JavaMain.main(JavaMain.java:4)

    我完全不知道这应该是什么意思,所以我反编译了生成的
    “.class”-文件:

    反编译的 A.class:
    public class A {
    
        public class B<T> {
            public /* synthetic */ A A$B$$$outer() {
                return A.this;
            }
    
            public B() {
                if (A.this == null) {
                    throw null;
                }
            }
        }
    
    }
    

    反编译的 A$B.class:
    public class A.B<T> {
        public /* synthetic */ A A$B$$$outer() {
            return A.this;
        }
    
        public A.B() {
            if (A.this == null) {
                throw null;
            }
        }
    }
    

    反编译的 JavaMain.class:
    public class JavaMain {
        public static void main(String[] arrstring) {
            A a;
            A a2 = a = new A();
            a2.getClass();
            A.B b = new A.B(a2, a);
        }
    }
    
    new A.B(a2, a)对我来说,这部分甚至看起来都不像是有效的 java(也不是
    javac)。所以,我本质上想说的是:一切
    崩溃和燃烧,我不知道为什么。因此,我会
    只是建议实现上述解决方法。

    希望有帮助。

    关于java - 如何从java实例化在scala中定义的嵌套泛型类?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48835404/

    相关文章:

    java - 最终和有效最终之间的区别

    java - if else 语句错误

    scala - distinct() 是否对数据集进行排序?

    java - CATCH 异常是否会报告给 Firebase/Fabric 等?

    postgresql - 升级数据库后应用程序无法在 Heroku 上运行

    scala - 映射和携带状态的正确 monad 或序列理解是什么?

    java - Spring Boot Restcontroller 作为单元测试中的内部类(webmvctest)

    java - 在Java嵌套类中,封闭类可以访问内部类的私有(private)成员吗?

    java - Spring boot - 首先初始化数据源

    java - “appletviewer”不被识别为内部或外部命令、可操作程序或批处理文件