这是一个有趣的问题。让我们先简化一下
static class Parent {
public int x = 1;
}
static class Child extends Parent {
public int x = 2;
}
public static void main(String[] args) {
Child c = new Child();
Parent p = c;
System.out.println(c.x);
System.out.println(p.x);
}
你认为这会打印什么?
它将是2 1Instance 变量被继承,但它们不可覆盖。这称为隐藏,Parent 的 x 被 Child 的 x 隐藏。实例变量是继承的,但它们是不可覆盖的。这叫做隐藏,Parent的x被Child的x隐藏了。
但是第二行……我们从哪里拉 x ?我们确实有一个 Parent 引用,但它指向一个 Child 实例,对吧?就像 Child 有两个同名的不同变量;一个它声明,一个它继承。可以?
Arrays.stream(Child.class.getFields())
.map(Field::getName)
.forEachOrdered(System.out::println); // x x
就像 Child 有两个完全限定的变量,根据引用,您可以访问一个或另一个。
现在来看你的例子(有点简化):
interface First {
int x = 3;
}
interface Second {
int x = 4;
}
static class Impl implements First, Second {}
你认为这会打印什么?
Arrays.stream(Impl.class.getFields())
.map(Field::getName)
.forEachOrdered(System.out::println);
和前面的例子一样,它会打印两次x,这个含义上面已经解释过了。
知道了这一点,让我们来回答你的问题(再次,有点简化):
interface First {
int x = 1;
}
interface Second {
int x = 2;
}
class Impl implements First, Second {
}
Field left = First.class.getField("x");
// you might think that this will fail, since Impl has two variables x...
Field right = Impl.class.getField("x");
if (!left.equals(right)) {
throw new AssertionError("Aren't they equal?");
}
按照现在的编写方式,这不会抛出AssertionError。
这里的想法是Field#equals 使用 3 个东西来表示相等:type、name 和 declaringClass。前两个显然匹配,所以我们只关心最后一个。这就是 JLS 提供帮助的地方which the other answer has already provided。
这就是顺便说一句的原因,如果您更改接口声明的顺序:class Impl implements Second, First 该代码将抛出 AssertionError。因为现在,x 将从Second 解析(按照声明的顺序...),但是我们将它与First 进行比较。