Java 超级接口运行时间差异 Java 8 vs Java 9

Java superinterfaces runtime difference Java 8 vs Java 9

我注意到当 运行 与 Java 8 和 Java 9 时,以下程序的输出有所不同。

import java.lang.reflect.Method;
public class OrderingTest {
    public static void main(String[] args) {
        ServiceImpl service = new ServiceImpl();
        for (Method method : service.getClass().getMethods()) {
            for (Class<?> anInterface : method.getDeclaringClass().getInterfaces()) {
                try {
                    Method intfMethod = anInterface.getMethod(method.getName(), method.getParameterTypes());
                    System.out.println("intfMethod = " + intfMethod);
                } catch (NoSuchMethodException e) { }
            }
        }
    }
}

class ServiceImpl implements ServiceX {
    @Override
    public Foo getType() { return null; }
}

interface ServiceX extends ServiceA<Foo>, ServiceB { }
abstract class Goo { }
class Foo extends Goo { }

interface ServiceA<S> {
    S getType();
}
interface ServiceB {
    @java.lang.Deprecated
    Goo getType();
}

您可以在此处 运行 java 的两个版本: https://www.jdoodle.com/online-java-compiler/

Java 8 个输出:

intfMethod = public abstract java.lang.Object ServiceA.getType()
intfMethod = public abstract java.lang.Object ServiceA.getType()
intfMethod = public abstract java.lang.Object ServiceA.getType()

Java 9 个输出:

intfMethod = public abstract Goo ServiceB.getType()
intfMethod = public abstract Goo ServiceB.getType()
intfMethod = public abstract Goo ServiceB.getType()

但是当我将超级接口重新排序为:

interface ServiceX extends ServiceB, ServiceA<Foo> { }

然后两个版本的java输出:

intfMethod = public abstract Goo ServiceB.getType()
intfMethod = public abstract Goo ServiceB.getType()
intfMethod = public abstract Goo ServiceB.getType()

我想知道是什么原因造成的?是否有我不知道的新 java 功能?

Java 8 个文档 https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.8

Java 9 个文档 https://docs.oracle.com/javase/specs/jls/se9/html/jls-8.html#jls-8.4.8

区别似乎在于 getMethod API 在使用中的实现,这在规定的 documentation starting Java-9 :

中可见

Within each such subset only the most specific methods are selected. Let method M be a method from a set of methods with same VM signature (return type, name, parameter types). M is most specific if there is no such method N != M from the same set, such that N is more specific than M. N is more specific than M if:

a. N is declared by a class and M is declared by an interface; or

b. N and M are both declared by classes or both by interfaces and N's declaring type is the same as or a subtype of M's declaring type (clearly, if M's and N's declaring types are the same type, then M and N are the same method).

虽然 Java-8 在内部跟进 interfaceCandidates.getFirst()(即这里的顺序更改很重要),但升级版本似乎在返回之前使用 res.getMostSpecific() 处理特定算法要求的方法。

从问题正文中移出答案:


根本原因是 java 类型擦除在编译时创建了一个具有 return 类型“对象”的桥接方法,它不如 return 类型的具体“粘性物”。 Java 9 更新了 Class.getMethod() 的实现,从 return 第一个定义接口的实现改为 return 最具体的实现。

foo@bar:~$ javap ./ServiceImpl.class
Compiled from "OrderingTest.java"
class ServiceImpl implements ServiceX {
  ServiceImpl();
  public Foo getType();
  public java.lang.Object getType(); <== here
  public Goo getType();
}

Java 8 中的旧功能可以在 Java 9 中通过添加另一个接口来维护,该接口使用 API 覆盖通用方法,使其更加具体。这将与 Java 9 一起使用,就像在 Java 8.

中一样
import java.lang.reflect.Method;
public class OrderingTest {
    public static void main(String[] args) {
        ServiceImpl service = new ServiceImpl();
        for (Method method : service.getClass().getMethods()) {
            for (Class<?> anInterface : method.getDeclaringClass().getInterfaces()) {
                try {
                    Method intfMethod = anInterface.getMethod(method.getName(), method.getParameterTypes());
                    System.out.println("intfMethod = " + intfMethod);
                } catch (NoSuchMethodException e) { }
            }
        }
    }
}

class ServiceImpl implements ServiceX {
    @Override
    public Foo getType() { return new Foo(); }
}

interface ServiceX extends ServiceAA, ServiceB { }
abstract class Goo { }
class Foo extends Goo { }
interface ServiceAA extends ServiceA<Foo> {
    @Override
    Foo getType();
}
interface ServiceA<S> {
    S getType();
}
interface ServiceB {
    @java.lang.Deprecated
    Goo getType();
}