如果你在一个类的子类中覆盖一个字段,那么子类就会有两个同名但类型不同的字段?

75

我有三个类:

public class Alpha {
    public Number number;
}

public class Beta extends Alpha {
    public String number;
}

public class Gama extends Beta {
    public int number;
}
为什么以下代码能够编译?为什么测试可以通过而没有运行时错误?
@Test
public void test() {
    final Beta a = new Gama();
    a.number = "its a string";
    ((Alpha) a).number = 13;
    ((Gama) a).number = 42;

    assertEquals("its a string", a.number);
    assertEquals(13, ((Alpha) a).number);
    assertEquals(42, ((Gama) a).number);
}

2
而且?它运作得不错。如果需要覆盖,应该使用setter/getter方法。公共字段几乎总是一个坏主意。 - kan
4个回答

96

成员变量不能像方法那样被覆盖。你的类 BetaGama 中的变量 number隐藏(而不是覆盖)超类的成员变量 number

通过强制转换,您可以访问超类中隐藏的成员变量。


11
请参阅《Java™教程》中的隐藏字段 - bigspawn

57

字段无法被覆盖;它们一开始就没有以多态的方式访问 - 每种情况下只是声明了一个新的字段。

它可以编译,因为在每种情况下,表达式的编译时类型足以确定你想要调用哪个名为number的字段。

在实际编程中,您可以通过两种方式避免这种情况:

  • 常识:属性重名会使您的代码更难阅读,所以请不要这样做
  • 可见性:如果将所有字段设置为私有,则子类也不会知道它们的存在

4

Java隐藏字段

当子类拥有与超类相同名称的字段时,这称为隐藏字段

Java的字段不支持多态性,也不考虑字段类型。

class A {
    String field = "A: field";

    String foo() {
        return "A: foo()";
    }
}

class B extends A {
    //B's field hides A's field
    String field = "B: field";

    String foo() {
        return "B: foo()";
    }
}

@Test
public void testPoly() {
    A a = new A();
    assertEquals("A: field", a.field);
    assertEquals("A: foo()", a.foo());

    B b = new B();
    assertEquals("B: field", b.field);
    assertEquals("B: foo()", b.foo());

    //B cast to A
    assertEquals("A: field", ((A)b).field);  //<--
    assertEquals("B: foo()", ((A)b).foo());
}

[Swift重写属性]


2
作为一种解决方法,你可以使用getter方法:
class A {
    private String field = "A: field";

    String getField() {
        return field;
    }
}

class B extends A {
    private String field = "B: field";

    @Override        
    String getField() {
        return field;
    }
}

你可能是想要使用@Override - RAM237

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接