给定一个类“Bar”,它扩展了实现“DeeDum”接口的类“Foo”
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";
}
为什么这不起作用?
public static Bar mybar = new Bar();
Assert.assertEquals("DEE", mybar.getDee());
Assert.assertEquals("DUM", mybar.getDum());
我得到“D”而是null。换句话说,Bar不会从Foo继承访问器,也不能覆盖属性。以某种方式调用mybar.getDum()调用类Foo的静态实例并从父类返回静态属性。即使在子类中重写属性!这是否意味着您无法继承任何方法或属性?
我无法绕过它。为什么Java不能继承访问者(为什么他们选择这样一个奇怪的选择呢?)
或者我只是做错了什么?
实际上,我仍然看到一些奇怪而不确定的东西。如果你有另一个类'Bar'扩展Foo并在初始化块中设置继承的访问器
虽然您可以在上面的块中设置父属性,但它实际上并不为子类创建副本。
这似乎是多个类的非确定性初始化。
因此,如果你有扩展foo和初始化块的Bar和Baz,它们似乎都继承了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
如果在基类Foo中设置了默认值,它仍然会有所不同。
我想我现在明白Bar和Baz中的初始化块实际上是设置Foo :: dee和Foo :: dum,但为什么声明上有区别?对我来说似乎是“未定义的”。
答案 0 :(得分:13)
问题在于,dee
中dum
的成员Foo
和Bar
的重复声明会隐藏Foo
的成员。 Bar
有拥有成员;那些Foo
永远不会被Bar
使用。你的意思是像
public class Bar extends Foo {
{
dee = "DEE";
dum = "DUM";
}
}
答案 1 :(得分:9)
您正在使用子类中定义的变量隐藏继承的变量。
public class Bar extends Foo {
public Bar() {
dee = "DEE";
dum = "DUM";
}
}
应该更好。
答案 2 :(得分:5)
当您致电mybar.getDee()
时,您正在调用Foo
基类中定义的方法。 (该方法 由Bar
继承。否则,您不会被允许首先在Bar
实例变量上调用它。)该方法返回dee
字段的值,但它是dee
类中定义的Foo
字段 - 定义方法本身的类。编译器在Foo
类中编译方法时解析了对该字段的引用。
其他一些答案使用了 override 这个词,通过在dee
中声明一个名为Bar
的字段来定义您所做的事情,但事实并非如此。您不能覆盖字段,因为字段不是虚拟的。也许你认为他们是。如果存在“虚拟字段”这样的事情,我也可能期望getDee()
返回字段的运行时类的版本(Bar
中的那个)而不是Foo
中的那个。编译方法时的范围({{1}})。但这并不是Java(或C#,或C ++,或Delphi,或我所知的任何其他语言)的工作方式。您习惯使用哪种语言?
答案 3 :(得分:2)
继承方法本身(例如getDee()
)是继承的,但实例变量不是。
如果实例变量可以在子类中被覆盖(就像你在这里尝试的那样),那么它会导致比修复更多的问题。
答案 4 :(得分:1)
访问者不是问题,而是字段。访问者指的是Foo.this.dee
,而不是Bar.this.dee
,它们是分开的。
答案 5 :(得分:1)
关于你的第二个“问题”......
实际上,我看到一些奇怪而不确定的东西......
...
“... System.out.println(baz.getDee()); //'DEE'但会期待'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?