Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

getDeclaredMethods() behaving differently in Java 7 vs. Java 8

Tags:

java

java-8

Consider the following little example:

package prv.rli.codetest;

import java.lang.reflect.Method;

public class BreakingInterfaces  {
    interface Base {
        BaseFoo foo();
        interface BaseFoo {           
        }
    }

    interface Derived extends Base {
        DerivedFoo foo();
        interface DerivedFoo extends BaseFoo {

        }
    }

    public static void main(String[] args) {       
        dumpDeclaredMethods(Derived.class);
    }

    private static void dumpDeclaredMethods(Class<?> class1) {
        System.out.println("---" + class1.getSimpleName() + "---");
        Method[] methods = class1.getDeclaredMethods();
        for (Method method : methods) {
            System.out.println(method);
        }
        System.out.println("----------");
    }
}

If you compile the above example with jdk1.7.0.55 the output is:

 ---Derived---
public abstract BreakingInterfaces$Derived$DerivedFoo BreakingInterfaces$Derived.foo()
----------

But when using jdk1.8.0.25 the output is:

---Derived---
public abstract prv.rli.codetest.BreakingInterfaces$Derived$DerivedFoo prv.rli.codetest.BreakingInterfaces$Derived.foo()
public default prv.rli.codetest.BreakingInterfaces$Base$BaseFoo prv.rli.codetest.BreakingInterfaces$Derived.foo()
----------

Does anybody know, whether this is a bug in jdk1.8.0.25 or why the public default Method resurfaces here?

like image 231
rli Avatar asked Nov 19 '14 09:11

rli


1 Answers

getDeclaredMethods() behaves correctly here as it tells you exactly what it has found in the class. If you feed in an interface compiled with Java 7 target (or an older compiler) you will see no difference to the output of the Java 7 implementation of getDeclaredMethods().

It’s the compiler which behaves differently. When compiling such a sub-interface in Java 8, a bridge method will be generated which will not be generated for a Java 7 target as it is not even possible for the Java 7 target.

The reason why bridge methods are generated for interfaces now is that you usually have more implementation classes than interfaces, therefore having a default bridge method in the interface saves you from adding that bridge method to every implementation. Further, it makes lambda class generation much easier if there is only one abstract method and no bridge method to implement.

When an interface hierarchy requires bridge methods but provides no defaults, the compiler has to generate code using LambdaMetafactory.altMetafactory rather than LambdaMetafactory.metafactory specifying every bridge method that is required.

like image 156
Holger Avatar answered Oct 09 '22 23:10

Holger