我正在尝试从Java实例化嵌套的通用Scala类,并遇到此编译错误.有人可以帮忙吗?谢谢
external.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: error: constructor Inner in class Outer.Inner cannot be applied to given types; Outer.Inner a = o.new Inner(); ^ required: Outer found: no arguments reason: actual and formal argument lists differ in length where A is a type-variable: A extends Object declared in class Outer.Inner 1 error
解决方法:
我看不到如何用Java做到这一点.有关详细信息,请参见附录.我将直接跳至变通方案.
每当尝试从Java调用Scala代码时遇到Java-Scala互操作的问题时,都有一个简单的,虽然可能是重量级的解决方法,但它基本上可以在每种情况下使用.
TL; DR
如果无法在Java代码中实例化Scala实体,则将所有内容隐藏在Java接口之后,并在Scala中实现此接口.
解决方法提案
如果您使用一些主流的Scala框架,则可能
具有完全独立的Java API(例如Spark,Akka,Play),然后
请使用它!
如果它是鲜为人知的Scala软件包,且没有单独的软件包
Java API,请执行以下操作:
>用纯Java编写最小的,干净的面向用户的API
>用Java提供面向框架的接口
>在Scala中实现Java接口
>提供Java API实现的入口点
>在Java代码中使用纯Java API
如果您不想实现完整的Java API,则可以在本地使用此方法来处理Scala代码中与Java项目无法无缝配合的部分.
为何起作用:Scala始终尝试适应Java.
Java完全忽略Scala.因此,这更容易
从Scala调用Java代码,并在其中实现Java接口
Scala,比其他方法要好.
这是将这种模式应用于您的示例的方式(我对其进行了扩展以使其变得平凡):
注意,类名有些丑陋,我这样做只是为了省略包声明,这样所有文件都可以转储到src / main / {scala,java};中.请勿在实际实现中执行此操作.
步骤0:查看第三方Scala库
假设这是我们的第三方Scala库,
具有superImportantMethods并计算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项目中使用这些接口,并实现它们
直接使用Scala:
interface A_j {
<X> B_j<X> createB(X x);
}
interface B_j<X> {
String superImportantMethod();
}
步骤3:在Scala中实现接口
/** 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)
}
步骤4:提供API的入口点
/** Some kind of entry point to the
* java API.
*/
object JavaApi {
def createA: A_j = new A_javaApiImpl
}
步骤5:在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编译器要求B构造函数使用类型为A的参数,
已经有些可疑了,不是很直观.
它已编译,但是当我尝试运行它时,收到以下神秘错误消息:
[错误]线程“主”中的异常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);
}
}
新的A.B(a2,a)
对我来说,部分甚至看起来都不像有效的Java(而且对
javac).所以,我本质上想说的是:一切
崩溃和烧伤,我不知道为什么.因此,我会
只需建议实施上述解决方法.
希望能有所帮助.