我正在尝试从 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 项目无缝协作的 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/