What this means is that if you have a method with a covariant (narrower) return type, the compiler will create a synthetic bridge method for you and invoke the overriding method through this bridge method. Covariance in return type was introduced in Java 5, at the same time as generics and it is implemented using bridge methods.
Example 1 :
Covariance in return type is also implemented using synthetic bridge methods even when there are no generics present.
For example:
abstract class A {
public abstract A get();
}
class B extends A {
@Override
public B get() {
return this;
}
}
Will be implemented in bytecode by the compiler using a synthetic bridge method that delegates to the original method. In principle you can imagine that the compiler translates the covariant override to this:
abstract class A {
public abstract A get();
}
class B extends A {
//bytecode only bridge method
@Override
public A get() {
return get;
}
public B get() {
return this;
}
}
Example 2 - Generics : Let's look at an example when generics are involved.
abstract class A<T> {
public abstract T get();
}
class B extends A<String> {
@Override
public String get() {
return "hello";
}
}
The method get()
of class B
is covariant in return type to the method get
in class A
. When compiling this bit of code the compiler will perform erasure which means that it will replace generics with their bounds and add casts to make sure that the everything works.
After erasure the classes look like this:
abstract class A {
public abstract Object get();
}
class B extends A {
@Override
public String get() {
return "hello";
}
}
Now since the method signature of get
is public Object get()
which is not a signature that is present in class B
the compiler will generate a bridge method in class B
in order to achieve the overriding.
You can think of class B
looking like the below. However, it is important to note that the below code will never be generated. It would not compile. The compiler just generates the equivalent get
method in bytecode.
class B extends A {
//bridge method
@Override
public Object get() {
return get();
}
public String get() {
return "hello";
}
}
Every polymorphic use of class B
through A
that invokes get
will invoke the bridge method which will delegate to
the real get method.