【问题标题】:Java not inheriting accessor methods?Java不继承访问器方法?
【发布时间】:2010-10-01 00:09:52
【问题描述】:

给定一个类“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”和空值。换句话说,Bar 不会从 Foo 继承访问器,也不能覆盖属性。以某种方式调用 mybar.getDum() 调用类 Foo 的静态实例并从父类返回静态属性。即使属性在子类中被覆盖!这是否意味着您不能继承任何方法或属性?

我无法绕过它。为什么 Java 不能继承访问器(为什么他们选择了这样一个奇怪的替代方案?)

还是我做错了什么?


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

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

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

因此,如果您有 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

如果在基类 Foo 中设置默认值,结果仍然不同。

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

【问题讨论】:

  • 喂食狂潮开始了……
  • 又是一个经典案例,“我不知道 X 是如何工作的,所以 X 肯定是完全错误的。”
  • “Java 学校不教 Java。十一点开始录音”。

标签: java inheritance


【解决方案1】:

问题是您在Bar 中对Foo 的成员deedum 的重复声明隐藏了Foo 的成员。 Bar 有其自己的 成员; Foo 的那些将永远不会被 Bar 使用。你的意思是这样的

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

【讨论】:

  • 恰恰相反。他的问题是他没有覆盖它们 - 他隐藏它们。
  • 是的,不好的例子。我试图为“Eclipse 编译器”编写一个简单的示例——去掉槽中的所有图标并假设它是正确的。
  • 对于我的下一个技巧,我将尝试通过在 MS Word 中写一篇关于“为什么独角兽比格里芬更酷”的文章来学习西班牙语,并让 clippy 纠正我的拼写和语法。
【解决方案2】:

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

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

应该会更好。

【讨论】:

    【解决方案3】:

    当您调用mybar.getDee() 时,您调用的是在Foo 基类中定义的方法。 (该方法 Bar 继承。否则,您首先不会被允许在Bar 实例变量上调用它。)该方法返回@ 的值987654325@ 字段,但它是在 Foo 类中定义的 dee 字段 — 定义方法本身的类。编译器在编译方法时解析了对字段的引用,在 Foo 类中。

    其他一些答案使用 override 这个词来定义您通过在 Bar 中声明一个名为 dee 的字段来定义您所做的事情,但事实并非如此。您不能覆盖字段,因为字段不是虚拟的。不过,也许你认为他们是。如果有“虚拟字段”之类的东西,我也可能期望 getDee() 返回运行时类的字段版本(Bar 中的那个),而不是当时范围内的那个该方法已编译(Foo's)。但这根本不是 Java(或 C#、C++、Delphi 或我知道的任何其他语言)的工作方式。你习惯什么语言,这在哪里会起作用?

    【讨论】:

      【解决方案4】:

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

      如果可以在子类中覆盖实例变量(就像您在此处尝试做的那样),则会导致比它修复的问题更多的问题。

      【讨论】:

      • 这是我想要弄清楚的。会造成什么问题?
      • 那么静态变量呢?
      • 嗯,我突然想到我要给出的答案介于“你不应该那样做”和“你为什么要那样做”之间。所以请原谅我,而我去搜索参考。 :)
      【解决方案5】:

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

      【讨论】:

        【解决方案6】:

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

        实际上,我看到了一些奇怪且不确定的东西......

        ...

        "...System.out.println(baz.getDee()); // 'DEE' 但期望 '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?

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-09-01
          • 1970-01-01
          • 2017-11-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多