2009-01-12 83 views
0

鉴于扩展类“富”,它实现了接口“DeeDum”Java不继承访问器方法?

public interface DeeDum { 
    public String getDee(); 
    public String getDum(); 
} 

public class Foo implements DeeDum { 
    public String dee = "D"; 
    public String dum; 

    public String getDee() { return dee; } 
    public String getDum() { return dum; } 
} 

public class Bar extends Foo { 
    public String dee = "DEE"; 
    public String dum = "DUM"; 
} 

为什么不这项工作一类“BAR”?

public static Bar mybar = new Bar(); 
Assert.assertEquals("DEE", mybar.getDee()); 
Assert.assertEquals("DUM", mybar.getDum()); 

我得到“D”,而不是null。换句话说,Bar不会从Foo继承访问器,并且不能覆盖属性。以某种方式调用mybar.getDum()调用类Foo的静态实例,并从父类返回静态属性。即使这些属性在子类中被覆盖!这是否意味着您不能继承任何方法或属性?

我无法将头围住它。为什么Java不能继承访问器(为什么他们会选择这样一个奇怪的选择?)

或者我只是做错了什么?


其实,我看到的东西仍然很奇怪,不确定。如果您有另一个扩展Foo的类'Bar'并将继承的访问器设置在初始化块中

虽然您可以在上面的块中设置父属性,但它实际上并不为子类创建副本。

它似乎是多个类的非确定性初始化。

所以如果你有Bar和Baz这两个都扩展foo并且有一个初始化块,它看起来像都继承了Bar设置的值。

public class Bar extends Foo { 
    { 
     dee = "dee"; 
     dum = "dum"; 
    } 
} 
public class Baz extends Foo { 
    { 
     dee = "DEE"; 
     dum = "DUM"; 
    } 
} 

public static Bar bar = new Bar(); 
public static Baz baz = new Baz(); 

System.out.println("mybaz: " + mybaz.getDee() + mybaz.getDum()); // DEEDUM 
System.out.println("mybar: " + mybar.getDee() + mybar.getDum()); // DEEDUM 

但是如果它们以不同的顺序正在被实例化,我得到:

public static Baz baz = new Baz(); 
public static Bar bar = new Bar(); 

System.out.println("mybaz: " + mybaz.getDee() + mybaz.getDum()); // deedum 
System.out.println("mybar: " + mybar.getDee() + mybar.getDum()); // deedum 

而且,如果一个默认基类美孚仍然设置不同的出来。

我想我现在明白Bar和Baz中的初始化块实际上是设置Foo :: dee和Foo :: dum,但是为什么声明中的区别?似乎“未定义”给我。

+0

而且狂热的开始.​​..... – 2009-01-12 19:55:58

+0

请不要喂巨魔 – Draemon 2009-01-12 20:07:43

+0

再一次的经典案例“我不知道* X *究竟如何运作,所以* X *必须完全错误。” – Bombe 2009-01-12 20:29:25

回答

2

访问器方法本身(例如getDee())是继承的,但实例变量不是。

如果实例变量可以在子类中被覆盖(就像你在这里试图做的那样),它会导致比修复更多的问题。

9

您正在隐藏继承的变量与在子类中定义的变量。

public class Bar extends Foo { 
    public Bar() { 
     dee = "DEE"; 
     dum = "DUM"; 
    } 
} 

应该工作得更好。

13

的问题是,你的成员deeBarFoodum的重复声明隐藏那些FooBar已有拥有成员;那些Foo永远不会被Bar使用。你的意思是类似于

public class Bar extends Foo { 
    { 
    dee = "DEE"; 
    dum = "DUM"; 
    } 
} 
1

访问器不是问题,它是字段。访问者指的是Foo.this.dee,而不是Bar.this.dee,它们是分开的。

5

当您拨打mybar.getDee()时,您正在调用Foo基类中定义的方法。 (该方法继承Bar。否则,您将不被允许在第一个地方的实例变量中调用它。)该方法返回dee字段的值,但它是dee字段中定义的Foo类 - 定义方法本身的类。编译器在Foo类中解析了方法编译时对该字段的引用。

其他一些答案都用这个词覆盖定义你没有通过声明名为deeBar场的关系,但是这并没有发生。由于字段不是虚拟的,因此无法覆盖字段。不过,也许你认为他们是。如果存在“虚拟域”这样的东西,我也可能期望getDee()返回运行时类的字段版本(Bar中的那个),而不是在编译方法时处于范围内的那个版本(Foo's)。但这并不是Java(或C#,或C++,或Delphi,或其他任何我知道的语言)的工作方式。你习惯于什么语言,在哪里工作?

1

至于你的第二个 “问题” ......

事实上,我看到一些奇怪和undeterministic仍...

...

“... System.out.println(baz.getDee()); //'DEE'but expect'dee'”

如果您在发布问题之前运行该程序以查看实际结果,将会非常有用。

这就是我得到的。正如您所期望的那样,它是“深入”的。

你可以看到它为你的自我创建的文件,编译它们并运行它们,如下图所示:

C:\oreyes\samples\java\dee>type DeeDum.java Foo.java Bar.java Baz.java Test.java 

DeeDum.java 


public interface DeeDum { 
    public String getDee(); 
    public String getDum(); 
} 

Foo.java 


public class Foo implements DeeDum { 
    public String dee = "D"; 
    public String dum; 

    public String getDee() { return dee; } 
    public String getDum() { return dum; } 
} 

Bar.java 


public class Bar extends Foo { 
    { 
     dee = "DEE"; 
     dum = "DUM"; 
    } 
} 

Baz.java 


public class Baz extends Foo { 
    { 
     dee = "dee"; 
     dum = "dum"; 
    } 
} 


Test.java 


class Test { 
    public static Bar bar = new Bar(); 
    public static Baz baz = new Baz(); 
    public static void main(String [] args) { 
     System.out.println(bar.getDee()); // 'DEE' 
     System.out.println(baz.getDee()); // 'DEE' but would expect 'dee' 
    } 
} 

C:\oreyes\samples\java\dee>javac *.java 

C:\oreyes\samples\java\dee>java Test 
DEE 
dee 

C:\oreyes\samples\java\dee> 

PEBKAC?