当使用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
发布于 2021-01-28 13:24:34
不同之处似乎在于正在使用的getMethod API的实现,声明的从Java-9开始的文档可以看到这种实现:
在每个这样的子集中,只选择最具体的方法。让方法M是一组具有相同VM签名的方法(返回类型、名称、参数类型)的方法。如果同一集合中没有这样的方法N != M,则M是最具体的,因此N比M更具体,如果: a. n由类声明,M由接口声明;或 b. n和M都是由类或两种接口声明的,而N的声明类型与M的声明类型相同或是M的声明类型的一个子类型(很明显,如果M和N的声明类型相同,则M和N是相同的方法)。
虽然Java-8在内部使用interfaceCandidates.getFirst()进行后续工作(即这里的顺序更改很重要),但升级后的版本在返回所要求的方法之前,似乎正在使用res.getMostSpecific()进行特定的算法研究。
发布于 2021-02-06 00:30:43
答案从问题正文中移出:
根本原因是在编译时java类型擦除创建了一个桥方法,返回类型为"Object",它没有返回类型"Goo“那么具体。Java9得到了Class.getMethod()实现的更新,从返回第一个定义接口的实现改为返回最具体的实现。
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();
}https://stackoverflow.com/questions/65937177
复制相似问题