Krystal Mok
2014-03-18 23:36:37 UTC
Hi all,
I'm curious about a corner case of binary compatibility with regard to
default methods and separate compilation (the source of many evils...). The
example is run with JDK 8 build 132.
Description:
The starting point is that I've got an interface and an implementation
class:
public interface IFoo<E> {
// empty
}
public class Foo implements IFoo<Foo> {
public void bar(Foo o) {
System.out.println("Foo.bar(Foo)");
}
}
Both the interface and the implementation are compiled into Class files.
Refer to this as ver1.
Foo won't have any bridge methods on it at this point.
Later on, the interface adds a default method, and is compiled separately,
without recompiling the implementation class:
public interface IFoo<E> {
default void bar(E e) {
System.out.println("IFoo.bar(E)");
}
}
Refer to this as ver2.
Then, another class uses the ver2 interface with the ver1 implementation
class:
public class Main {
public static void main(String[] args) {
Foo f = new Foo();
f.bar(f); // (1) invokevirtual Foo.bar(Foo)void
IFoo<Foo> i = f;
i.bar(f); // (2) invokeinterface IFoo.bar(Object)void
}
}
The output at (1) and (2) are:
Foo.bar(Foo)
IFoo.bar(E)
My question is: is this the expected behavior according to the current
spec, by design? I suppose it is.
The implementation in HotSpot seems to be only using the erased (name,
signature) of bar(Object)void when searching candidates, so naturally it
won't find the Foo.bar(Foo), and it can't find a bridge method for that
because of separate compilation.
Thanks,
Kris
I'm curious about a corner case of binary compatibility with regard to
default methods and separate compilation (the source of many evils...). The
example is run with JDK 8 build 132.
Description:
The starting point is that I've got an interface and an implementation
class:
public interface IFoo<E> {
// empty
}
public class Foo implements IFoo<Foo> {
public void bar(Foo o) {
System.out.println("Foo.bar(Foo)");
}
}
Both the interface and the implementation are compiled into Class files.
Refer to this as ver1.
Foo won't have any bridge methods on it at this point.
Later on, the interface adds a default method, and is compiled separately,
without recompiling the implementation class:
public interface IFoo<E> {
default void bar(E e) {
System.out.println("IFoo.bar(E)");
}
}
Refer to this as ver2.
Then, another class uses the ver2 interface with the ver1 implementation
class:
public class Main {
public static void main(String[] args) {
Foo f = new Foo();
f.bar(f); // (1) invokevirtual Foo.bar(Foo)void
IFoo<Foo> i = f;
i.bar(f); // (2) invokeinterface IFoo.bar(Object)void
}
}
The output at (1) and (2) are:
Foo.bar(Foo)
IFoo.bar(E)
My question is: is this the expected behavior according to the current
spec, by design? I suppose it is.
The implementation in HotSpot seems to be only using the erased (name,
signature) of bar(Object)void when searching candidates, so naturally it
won't find the Foo.bar(Foo), and it can't find a bridge method for that
because of separate compilation.
Thanks,
Kris