12

我有一个应该实现公共字段的抽象类,这个字段是一个接口或另一个抽象类。

像这样的东西:

public abstract class GenericContainer {
    public GenericChild child;
}

public abstract class GenericChild {
    public int prop1=1;
}

public abstract class SpecialChild extends GenericChild {
    public int prop1=2;
}

现在我有另一个专门的类容器:

public abstract class SpecialContainer extends GenericContainer {
    public SpecialChild child=new SpecialChild(); //PAY ATTENTION HERE!
}

Java 允许我编译它,并且我想象中的字段childSpecialContainer自动重载...的字段childGenericContainer问题是:我是对的吗?孩子的自动“超载”会发生吗?

而且,更重要的问题是,如果我还有这样的课程:

public class ExternalClass {
    public GenericContainer container=new SpecialContainer();
    public int test() {
         return container.child.prop1
    }
}

test()会返回 1 还是 2?我的意思是GenericContainer容器字段prop1,通用的还是特殊的?如果特殊的 prop1 被声明为 String (是的,java 允许我在这种情况下也可以编译)怎么办?

谢谢!

4

6 回答 6

15

在 Java 中,数据成员/属性不是多态的。重载意味着字段将具有不同的值,具体取决于访问的类。子类中的字段会隐藏超类中的字段,但两者都存在。字段基于引用类型调用,而方法使用实际对象。你可以自己试试。

它被称为变量隐藏/阴影,有关更多详细信息,请参见此处

于 2013-03-09T14:29:23.553 回答
12

它没有覆盖任何东西,您只是将原始字段隐藏在当前类范围内。如果您使用带有子类型的变量,您仍然可以访问原始属性。例子:

abstract class GenericContainer {
    public GenericChild child;       
}

abstract class GenericChild {
    public int prop1=1 ;
}

class SpecialChild extends GenericChild {
    public int prop1=2;
}

class SpecialContainer extends GenericContainer {
    public SpecialChild child;
}

public class Main {

    public static void main( String ... args ) {

        GenericContainer container = new SpecialContainer();
        container.child = new SpecialChild();

        System.out.println( container.child.prop1 );

        SpecialChild child = (SpecialChild) container.child;        
        System.out.println( child.prop1 );
    }

}

这会打印 1,然后打印 2。

SpecialChild还可以使用以下方法提升一级super

class SpecialChild extends GenericChild {
    public int prop1=2;

    public int getOriginalProp1() {
        return super.prop1;
    }

}
于 2013-03-09T14:33:45.863 回答
2

关于

....我想象一下 SpecialContainer 中的“child”字段会自动重载 GenericContainer 的“child”字段...

不,字段不会被覆盖,只有方法会。

这就是为什么首选使用(可覆盖的)getter 和 setter 方法而不是直接访问字段的原因之一。您的字段几乎都应该是私有的。

至于您的设计,您的 SpecialContainer 类不需要具有 SpecialChild 字段,而是应该将 SpecialChild 对象放在 GenericChild 字段中。

于 2013-03-09T14:27:21.030 回答
2

为什么没有人观察该程序会抛出NullPointerException.
具有相同名称的子类field将隐藏超类的field. 没有overridingwith field。覆盖只能使用方法。

作者的原始代码:

public abstract class GenericContainer {
    public GenericChild child;
}

public abstract class GenericChild {
    public int prop1=1;
}

public abstract class SpecialChild extend GenericChild {
    public int prop1=2;
}

public abstract class SpecialContainer extends GenericContainer {
    public SpecialChild child=new SpecialChild(); //PAY ATTENTION HERE!
}

public class ExternalClass {
    public GenericContainer container=new SpecialContainer();
    public int test() {
         return container.child.prop1
    }
}
于 2013-03-09T15:25:48.067 回答
1

为了回答您的问题,它维护了这两个实例。并且根据您引用容器的方式(通过抽象或 impl)确定您引用的变量。

public class Test {

   public abstract class Container{
       public Generic gen = new Generic();
   }

   public class ContainerImpl extends Container{
       public GenericImpl gen = new GenericImpl();
   }

   public class Generic{
       public int prop = 0;
   }

   public class GenericImpl extends Generic{
       public int prop = 1;
   }

   public Test(){
       Container c = new ContainerImpl();
       System.out.println(c.gen.prop); // Outputs "0"
       System.out.println(((ContainerImpl)c).gen.prop); // Output "1"
   }

   public static void main(String[] args) {
      new Test();
   }

}

手头更大的问题是,你为什么要设计这样的东西?我假设你是从理论的角度来问的。

我的 2 美分,这不是很好的 OO 设计。您最好将公共变量设为私有并通过构造函数或属性设置器分配它们的值。照原样,它将导致您的代码出现意外结果。

于 2013-03-09T14:43:40.080 回答
1

Java允许我编译这个,我想象SpecialContainer中的“child”字段自动重载GenericContainer的“child”字段......

首先,继承不适用于变量。字段(Insatnce 变量)不会在您的子类中被覆盖。它们仅在您的子类中标有 时才可见public, protected or default

于 2013-03-09T14:27:54.813 回答