Decompile Scala code: why there are two overridden methods in the derived class?
class A
{
private var str: String = "A"
val x: A = this
override def toString(): String = str
def m1(other: AnyRef): AnyRef = {
println("This is A.m1(AnyRef)")
other
}
}
class B extends A {
private var str: String = "B"
var z: Int = 0
override val x: B = this
override def m1(other: AnyRef): B = {
println("This is B.m1(AnyRef)")
this
}
}
class B of the above code is decompiled as:
public class test$B extends test$A {
private java.lang.String str;
private int z;
private final test$B x;
private java.lang.String str();
private void str_$eq(java.lang.String);
public int z();
public void z_$eq(int);
public test$B x();
public test$B m1(java.lang.Object);
public java.lang.Object m1(java.lang.Object);
public test$A x();
public test$B();
}
I cannot understand why there are two "versions" of method m1
in the decompiled code.
From my understanding, B.m1
just overrides A.m1
and public java.lang.Object m1(java.lang.Object)
belongs to A
and should not
be in class B
.
This is a synthetic bridge method.
In Java bytecode, methods only override methods with the exact same signature. If B did not have any instance of Object m1(Object)
, then any attempts to call it would call the implementation in A instead, which is not what you want. Therefore, the compiler inserts a synthetic bridge method which simply calls B m1(Object)
. This behavior is not specific to Scala - it happens in pure Java as well.
You can look at it in more detail by examining the disassembly. If I compile and diassemble the following code
class A
{
def m1(other: AnyRef): AnyRef = {
println("This is A.m1(AnyRef)")
other
}
}
class B extends A {
override def m1(other: AnyRef): B = {
println("This is B.m1(AnyRef)")
this
}
}
The relevant parts of B are
.method public m1 : (Ljava/lang/Object;)LB;
.code stack 2 locals 2
L0: getstatic Field scala/Predef$ MODULE$ Lscala/Predef$;
L3: ldc 'This is B.m1(AnyRef)'
L5: invokevirtual Method scala/Predef$ println (Ljava/lang/Object;)V
L8: aload_0
L9: areturn
L10:
.end code
.methodparameters
other final
.end methodparameters
.end method
.method public bridge synthetic m1 : (Ljava/lang/Object;)Ljava/lang/Object;
.code stack 2 locals 2
L0: aload_0
L1: aload_1
L2: invokevirtual Method B m1 (Ljava/lang/Object;)LB;
L5: areturn
L6:
.end code
.methodparameters
other final
.end methodparameters
.end method
As you can see, the method m1 (Ljava/lang/Object;)Ljava/lang/Object;
simply forwards the arguments to m1 (Ljava/lang/Object;)LB;
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With