4

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

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 时,这是输出

大在这里:告诉你,大

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

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

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

4

6 回答 6

6

Fields are not virtual, unlike methods. For this reason, it is a bad idea to declare fields with the same name as a field in another class in the hierarchy. The field referred to in theMethod is always going to be from Big (i.e. when you declare a field with the same name, it just hides the old field when in the scope of the replacing class, but doesn't replace it).

One solution would be to override a method that gets the field from the current class:

In theMethod replace the tellMe field with getTellMe() and for all classes override getTellMe() to return the correct value (or the field that hides the superclass's field).

于 2012-07-31T16:43:51.097 回答
2

You can overwrite the value of Big.tellMe in the constructor of Little.

get rid of:

public String tellMe = "little";

and change the Little constructor to:

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

at that point, you can get rid of Little.tellMe() also.

于 2012-07-31T16:43:28.823 回答
2

正如Java 文档所述,您正在做的是隐藏超类字段,而不是覆盖它。它还指出,这样做不是一个好主意。

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

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

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

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

于 2012-07-31T16:53:42.967 回答
1

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

public abstract String tellMeString();

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

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

然后该方法可以执行:

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

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

于 2012-07-31T16:47:29.603 回答
1

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

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

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”:超类中的变量在构造对象时被初始化;超类中定义的方法返回了这个变量。

于 2012-07-31T16:47:31.833 回答
1

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

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() 看到大版本的字段, Small.doIt2() 版本看到小版本的字段。

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

于 2012-07-31T17:34:37.910 回答