Dan*_*ini 3 java inheritance scala bytecode subtyping
我从这个问题中知道 Scala 为trait诸如
trait A {
def a = { ... }
}
Run Code Online (Sandbox Code Playgroud)
类似于以下 Java 代码的结构
public interface A {
public void a();
}
public class A$class {
public static void a(A self) { ... }
}
Run Code Online (Sandbox Code Playgroud)
但是,在 Scala 中, atrait可以扩展 a class:
class B {
def b = { ??? }
}
trait A extends B {
def a = { ??? }
}
Run Code Online (Sandbox Code Playgroud)
这在 Java 等价物中如何翻译,其中接口不能从类继承?是否为 生成了额外的接口B?这个 Scala 特性对 Java 互操作性有什么影响吗?
嗯,你可以编译
class B {
def b = { ??? }
}
trait A extends B {
def a = { ??? }
}
Run Code Online (Sandbox Code Playgroud)
并检查字节码:
> javap -c -l -p B
Compiled from "B.scala"
public class B {
public scala.runtime.Nothing$ b();
Code:
0: getstatic #16 // Field scala/Predef$.MODULE$:Lscala/Predef$;
3: invokevirtual #19 // Method scala/Predef$.$qmark$qmark$qmark:()Lscala/runtime/Nothing$;
6: areturn
LineNumberTable:
line 2: 0
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this LB;
public B();
Code:
0: aload_0
1: invokespecial #25 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 4: 0
line 1: 4
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LB;
}
Run Code Online (Sandbox Code Playgroud)
> javap -c -l -p A
Compiled from "B.scala"
public interface A {
public static scala.runtime.Nothing$ a$(A);
Code:
0: aload_0
1: invokespecial #15 // InterfaceMethod a:()Lscala/runtime/Nothing$;
4: areturn
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 $this LA;
public scala.runtime.Nothing$ a();
Code:
0: getstatic #22 // Field scala/Predef$.MODULE$:Lscala/Predef$;
3: invokevirtual #25 // Method scala/Predef$.$qmark$qmark$qmark:()Lscala/runtime/Nothing$;
6: areturn
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this LA;
public static void $init$(A);
Code:
0: return
LineNumberTable:
line 4: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 $this LA;
}
Run Code Online (Sandbox Code Playgroud)
正如你看到scalac做A只是默认方法的接口(他们是因为Java的8个可用)。你可能想知道,如果你想调用某个B方法会发生什么,A因为我们A extends B在字节码中看不到它:
trait A extends B {
def a = { b; ??? }
}
Run Code Online (Sandbox Code Playgroud)
现在 A 改为:
> javap -c -l -p A
Compiled from "B.scala"
public interface A {
public static scala.runtime.Nothing$ a$(A);
Code:
0: aload_0
1: invokespecial #15 // InterfaceMethod a:()Lscala/runtime/Nothing$;
4: areturn
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 $this LA;
public scala.runtime.Nothing$ a();
Code:
0: aload_0
1: checkcast #18 // class B
4: invokevirtual #21 // Method B.b:()Lscala/runtime/Nothing$;
7: athrow
LineNumberTable:
line 5: 0
LocalVariableTable:
Start Length Slot Name Signature
0 8 0 this LA;
public static void $init$(A);
Code:
0: return
LineNumberTable:
line 4: 0
LocalVariableTable:
Start Length Slot Name Signature
0 1 0 $this LA;
}
Run Code Online (Sandbox Code Playgroud)
正如我们所看到的,代码使用checkcasts 进行this转换B,然后调用 'B 的方法 - 这意味着scalac必须确保然后实例化A它也是扩展的东西B!所以让我们检查一下当我们这样做时会发生什么:
class B {
def b = { ??? }
}
trait A extends B {
def a = { b; ??? }
}
class C extends A
Run Code Online (Sandbox Code Playgroud)
而 C 是
> javap -c -l -p C
Compiled from "B.scala"
public class C extends B implements A {
public scala.runtime.Nothing$ a();
Code:
0: aload_0
1: invokestatic #16 // InterfaceMethod A.a$:(LA;)Lscala/runtime/Nothing$;
4: areturn
LineNumberTable:
line 7: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this LC;
public C();
Code:
0: aload_0
1: invokespecial #22 // Method B."<init>":()V
4: aload_0
5: invokestatic #26 // InterfaceMethod A.$init$:(LA;)V
8: return
LineNumberTable:
line 7: 0
LocalVariableTable:
Start Length Slot Name Signature
0 9 0 this LC;
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,这与字节码的最终结果非常依赖于上下文 - 编译器将处理各种事情以使其最终适合事物,但前提是它知道您如何使用它。
因此,如果您想要与 Java 的互操作性,请坚持使用简单的traits 和classes 作为您的通用接口,并让 Scala 实例化更复杂的实现。从 Java 自己做这件事可能会以许多意想不到的方式咬你。