【问题标题】:Variable Inheritance in JavaJava中的变量继承
【发布时间】:2012-07-31 16:36:45
【问题描述】:

我有一个 Super 类和一堆子类。我希望在每个子类中都有一个具有相同名称的字段,但我不希望它在超类中定义,或者至少我不想使用该值。这就是我现在所拥有的

public abstract class Big {

    public String tellMe = "BIG";

    public Big() {}

    public void theMethod() {
        System.out.println ("Big was here: " + tellMe() + ", " + tellMe);
    }
    public String tellMe() {
        return tellMe;
    }
}

public class Little extends Big{

    public String tellMe = "little";
    public Little(){}

    public String tellMe() {
        return "told you";
    }
    public static void main(String [] args) {
        Little l = new Little();
        l.theMethod();
    }
}

当我运行 Little 时,这是输出

Big 在这里:告诉过你,BIG

我不确定为什么在tellMe 指的是“BIG”时打印出“告诉你”。怎么可能两者都是真的?

我的问题是我希望方法 tellMe() 在 Big 中,并在所有子类中定义变量 tellMe(它将实际返回)。我可以让它工作的唯一方法是正如我所写的那样,通过重写每个子类中的 tellMe() 方法。但这不是违背了继承的全部目的吗???请帮忙

编辑:我不在我的子类中使用构造函数。我想要的只是一个可以在所有子类中设置的字段和一个使用这些值的超类中的方法。我不明白为什么这是不可能的,因为每个子类都必须实现它,所以这是有道理的......如果这根本不可能,请告诉我

【问题讨论】:

  • 方法tellMe()和字段tellMe绝对没有关联,不管名字。
  • 我知道...我不明白此评论的目的,但谢谢

标签: java eclipse oop inheritance


【解决方案1】:

与方法不同,字段不是虚拟的。因此,在层次结构中声明与另一个类中的字段同名的字段是一个坏主意。 theMethod 中引用的字段总是来自Big (即,当您声明具有相同名称的字段时,它只是在替换类的范围内隐藏旧字段,但不会替换它)。

一种解决方案是重写从当前类获取字段的方法:

theMethod 中,将tellMe 字段替换为getTellMe(),并为所有类覆盖getTellMe() 以返回正确的值(或隐藏超类字段的字段)。

【讨论】:

  • 这是我的问题。当继承的目的是压缩代码时,为什么有必要在我的子类中创建数百次完全相同的方法......为什么没有抽象字段之类的东西,我可以在 Big 中定义并在所有我的小宝贝?
  • getTellMe(){return "Text";} 并不比tellMe="Text"; 长多少。这只是 JVM 的设计:字段不是虚拟的。声明和隐藏一堆字段肯定更糟糕:为每个字段的每个实例分配内存。请参阅 Keith Randall 的解决方案 - 他更改字段而不是声明新字段。
  • 我不在我的子类中使用构造函数。我还希望能够直接编辑此字段...所以我会创建一个字段,然后进行获取和设置。在每个子类中......我不明白为什么这是必要的,因为我在上面发布的原因。它比只制作一个字段要长得多
  • 嗯,他的解决方案要长一点。您可能需要为您的问题添加更多信息;关于你实际想要完成的事情,我们只能假设这么多。如果字段的值是唯一的区别,那么我建议从类的用户设置字段。
  • “从班级的用户那里设置字段”是什么意思。
【解决方案2】:

您可以在Little的构造函数中覆盖Big.tellMe的值。

摆脱:

public String tellMe = "little";

并将Little 构造函数更改为:

public Little(){
    tellMe = "little";
}

此时,您也可以摆脱Little.tellMe()

【讨论】:

  • +1。我喜欢公共字段是最终的,但如果您信任程序员,这是一个很好的解决方案。
  • 不,这不是一个好的解决方案。当继承的全部目的是压缩代码时,为什么我必须将确切的 smae 行放在数百个子类中?我什至不使用构造函数,所以这对我没有帮助
  • 嗯?与声明和隐藏字段的解决方案相比,此解决方案所需的代码更少。
  • @ColeCanning:您只需向每个子类添加 一个 行。我看不出你会做得比这更好。
  • 我根本不在我的子类中使用构造函数。我也知道我提供的代码是浪费和荒谬的,这就是我在这里的原因。我必须提供我认为最接近我需要的方式
【解决方案3】:

您正在做的是隐藏超类字段,而不是覆盖它,如Java documentation states。 并且还表示这样做不是一个好主意。

因此,动态查找不会像方法那样工作。如果从子类中读取变量,它将采用“其”字段值。 在头等舱,另一个。

你可以在 Java 中覆盖的是行为,所以我建议的是 定义一个方法

public String tellMe() {
  return "Whatever";
}

您可以在子类中覆盖以返回您需要的任何字符串。

【讨论】:

    【解决方案4】:

    您可以在 Big 中创建一个函数,而不是在 Big 中定义 tellMe(因为您说您不想在 Big 中定义/使用该值):

    public abstract String tellMeString();
    

    并像这样在每个子类中定义它(对于 Little):

    public String tellMeString()
    {
        return "Little";
    }
    

    那么该方法就可以执行了:

    System.out.println ("Big was here: " + tellMe() + ", " + tellMeString());
    

    在这种情况下,您根本不必定义变量“tellMe”,只需在每个子类中覆盖 tellMeString 即可返回不同的字符串。

    【讨论】:

    • “并像这样在每个子类中定义它(对于 Little):”这就是我所做的,也是我遇到的问题。当继承点是压缩代码时,为什么我要在每个子类中使用相同的方法?
    • 这里的想法是该方法的执行方式因子类而异。您希望每个子类为同一个函数提供不同的值,而继承通过允许每个子类提供自己的实现来提供解决方案。
    • 是的,我希望每个子类都为 THE SAME FUNCTION 提供不同的值……为什么我要让每个子类都提供自己的、编写相同的子类?
    • 函数在超类中声明,由子类定义。超类不知道从它继承的任何类的细节。例如,我可以使用你的代码,创建我自己的 Big 子类,然后做任何我想做的事情。你的超类如何知道我的子类包含什么?你如何编程所有的可能性?同样通过使用方法来访问数据,将来重构代码会容易得多,因为类中有一个定义良好的入口点,并且这个入口点(函数)可以更改并保持相同的签名。
    • "你的超类怎么知道我的子类包含什么?"这正是抽象领域允许我做的事情。我不知道为什么它们不存在......哦,好吧。这也将使重构变得更加容易,因为超类中只有一个方法,并且所有子类都有一个变量,而不是我必须经历相同的方法签名并对
    【解决方案5】:

    字段未按预期继承。您可以从子类访问超类的字段(除非它是私有的)。但是您不能“覆盖”字段。这就是为什么在超类Big 中实现的方法使用的tellMe 使用在同一类中定义的变量。

    如果你想要继承使用方法。例如,您可以实现方法“tellMe()”,在超类中返回"BIG",在子类中返回"little"

    class Big {
        protected String tellMe() {
            return "BIG";
        }
    }
    class Little {
        @Override
        protected String tellMe() {
            return "Little";
        }
    }
    

    您也可以在构造函数中初始化变量tellMe

    class Big {
        private String tellMe;
        public Big() {
            this("BIG");
        }
        protected Big(String tellMe) {
            this.tellMe = tellMe;
        }
        protected String tellMe() {
            return "BIG";
        }
    }
    class Little {
        public Little() {
             super("Little");
        }
    }
    

    现在new Little().tellMe() 将返回“Little”:超类中的变量在构造对象时被初始化;超类中定义的方法返回了这个变量。

    【讨论】:

    • 感谢您的回答。我仍然不明白为什么不能抽象地声明字段,这对我来说很有意义
    【解决方案6】:

    方法可以被覆盖,字段在它们被调用的范围内是可见的。

    static class Big {
        String field = "BIG";
        String bark()   { return "(big bark)"; }
    
        void doIt() {
            System.out.format("field(%s) bark(%s)\n", field,bark());
        }
        void doIt2()    {
            System.out.format("2:field(%s) bark(%s)\n", field,bark());
        }
    }
    
    static class Small extends Big {
        String field = "small";
        String bark()   { return "(small bark)"; }
        void doIt2()    {
            System.out.format("2:field(%s) bark(%s)\n", field,bark());
        }
    }
    
    public static void main(String... args) {
    
        Big b = new Big();
        b.doIt();
        b.doIt2();
    
        Small s = new Small();
        s.doIt();
        s.doIt2();
    }
    

    输出是:

    field(BIG) bark((big bark))
    2:field(BIG) bark((big bark))
    field(BIG) bark((small bark))
    2:field(small) bark((small bark))
    

    由于 doIt() 是在 Big 类中定义的,所以它总是会看到 Big 版本的字段。 doIt2() 在 Big 中定义,但在 Small 中被覆盖。 Big.doIt2() 看到的是 Big 版本的字段,Small.doIt2() 版本看到的是 Small 版本的字段。

    正如其他人指出的那样,这样做是一个非常糟糕的主意 - 更好的方法是在子类构造函数中设置新值,或者使用被覆盖的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-19
      • 1970-01-01
      • 2019-04-28
      • 2015-09-14
      • 1970-01-01
      • 2011-04-24
      相关资源
      最近更新 更多