Java不继承访问器方法?

时间:2009-01-12 19:45:57

标签: java inheritance

给定一个类“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,但为什么声明上有区别?对我来说似乎是“未定义的”。

6 个答案:

答案 0 :(得分:13)

问题在于,deedum的成员FooBar的重复声明会隐藏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?