Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is an interface with a default method initialized?

While searching through the Java Language Specification to answer this question, I learned that

Before a class is initialized, its direct superclass must be initialized, but interfaces implemented by the class are not initialized. Similarly, the superinterfaces of an interface are not initialized before the interface is initialized.

For my own curiosity, I tried it and, as expected, the interface InterfaceType was not initialized.

public class Example {     public static void main(String[] args) throws Exception {         InterfaceType foo = new InterfaceTypeImpl();         foo.method();     } }  class InterfaceTypeImpl implements InterfaceType {     @Override     public void method() {         System.out.println("implemented method");     } }  class ClassInitializer {     static {         System.out.println("static initializer");     } }  interface InterfaceType {     public static final ClassInitializer init = new ClassInitializer();      public void method(); } 

This program prints

implemented method 

However, if the interface declares a default method, then initialization does occur. Consider the InterfaceType interface given as

interface InterfaceType {     public static final ClassInitializer init = new ClassInitializer();      public default void method() {         System.out.println("default method");     } } 

then the same program above would print

static initializer   implemented method 

In other words, the static field of the interface is initialized (step 9 in the Detailed Initialization Procedure) and the static initializer of the type being initialized is executed. This means that the interface was initialized.

I could not find anything in the JLS to indicate that this should happen. Don't get me wrong, I understand that this should happen in case the implementing class doesn't provide an implementation for the method, but what if it does? Is this condition missing from the Java Language Specification, did I miss something, or am I interpreting it wrongly?

like image 850
Sotirios Delimanolis Avatar asked Apr 15 '14 23:04

Sotirios Delimanolis


People also ask

What is default method in interface?

Default methods enable you to add new functionality to existing interfaces and ensure binary compatibility with code written for older versions of those interfaces. In particular, default methods enable you to add methods that accept lambda expressions as parameters to existing interfaces.

What is the use of default method in interface in Java 8?

Java 8 introduces a new concept of default method implementation in interfaces. This capability is added for backward compatibility so that old interfaces can be used to leverage the lambda expression capability of Java 8. For example, 'List' or 'Collection' interfaces do not have 'forEach' method declaration.

What is the use of default and static method in interface?

Default methods enable you to add new functionality to the interfaces of your libraries and ensure binary compatibility with code written for older versions of those interfaces. A static method is a method that is associated with the class in which it is defined rather than with any object.

Can an interface be initialized?

You do not 'initialize' an interface. If you create a class that 'implements' an interface, you are creating a class that gives an implementation for every abstract method that is in that interface.


1 Answers

This is a very interesting issue!

It seems like JLS section 12.4.1 ought to cover this definitively. However, the behavior of Oracle JDK and OpenJDK (javac and HotSpot) differs from what's specified here. In particular, the Example 12.4.1-3 from this section covers interface initialization. The example as follows:

interface I {     int i = 1, ii = Test.out("ii", 2); } interface J extends I {     int j = Test.out("j", 3), jj = Test.out("jj", 4); } interface K extends J {     int k = Test.out("k", 5); } class Test {     public static void main(String[] args) {         System.out.println(J.i);         System.out.println(K.j);     }     static int out(String s, int i) {         System.out.println(s + "=" + i);         return i;     } } 

Its expected output is:

1 j=3 jj=4 3 

and indeed I get the expected output. However, if a default method is added to interface I,

interface I {     int i = 1, ii = Test.out("ii", 2);     default void method() { } // causes initialization! } 

the output changes to:

1 ii=2 j=3 jj=4 3 

which clearly indicates that interface I is being initialized where it wasn't before! The mere presence of the default method is enough to trigger the initialization. The default method doesn't have to be called or overridden or even mentioned, nor does the presence of an abstract method trigger initialization.

My speculation is that the HotSpot implementation wanted to avoid adding class/interface initialization checking into the critical path of the invokevirtual call. Prior to Java 8 and default methods, invokevirtual could never end up executing code in an interface, so this didn't arise. One might think this is part of the class/interface preparation stage (JLS 12.3.2) which initializes things like method tables. But perhaps this went too far and accidentally did full initialization instead.

I've raised this question on the OpenJDK compiler-dev mailing list. There's been a reply from Alex Buckley (editor of the JLS) in which he raises more questions directed at the JVM and lambda implementation teams. He also notes that there's a bug in the spec here where it says "T is a class and a static method declared by T is invoked" should also apply if T is an interface. So, it might be that there are both specification and HotSpot bugs here.

Disclosure: I work for Oracle on OpenJDK. If people think this gives me an unfair advantage at getting the bounty attached to this question, I'm willing to be flexible about it.

like image 50
Stuart Marks Avatar answered Sep 28 '22 21:09

Stuart Marks