访问 child class 中的覆盖值
Accessing overridden values in child class
假设我有一个摘要 parent class:
public abstract class Bar {
public int count = 0;
}
现在我有几个具体的子class。假设一个是:
public class Foo extends Bar {
public int count = 1;
}
现在我想遍历扩展抽象 class 的 children 集合。但是,当我询问它们的 count
值时,我发现我得到的是 parent 的初始化值,而不是 child 中初始化的值:
List<Bar> l = new ArrayList<Bar>();
l.add(new Foo());
for (Bar b : l) {
System.out.println(b.count.toString()); // This always gives 0.
}
我显然用错了 class 模型。正确的方法是什么,以便我可以获得由 subclasses 初始化的值?
您的迭代引用类型 Bar
,作为您的 List
.
的参数化类型
因此它将获得 Bar
的 count
属性,被 Foo
的 count
隐藏。
这就是为什么你得到 0
。
如果您想要动态变量解析,请实现并覆盖返回 count
的方法。
如果您还使用更合适的访问修饰符设置 count
,这反过来将有助于您的封装设计。
最后的笔记
引用您的评论:
I prefer sticking to fields, because I have a lot of these fields
为您的字段实现 getter 和 setter 应该是微不足道的。
事实上,一旦您声明了您的字段,您只需按照任何受人尊敬的 IDE 中的快速向导,即可获得自动草稿或完全实施的方法。
作为一般规则,如果您的字段是可变的,您可以考虑不按原样使用模板吸气剂。
作为另一条一般规则,如果您的字段在设置前需要验证,您可以考虑不使用按原样实现的模板设置器。
我建议您仔细研究 encapsulation 的概念。
作为 Mena 回答的补充,我想指出在子类中隐藏超类属性通常是一团糟,显然不是一个好的面向对象设计。
如果您希望 Foo
和 Bar
对象具有不同的值,为什么不使用构造函数以不同方式初始化 count
字段?这是实现您要求的最直接的方法。
public class Bar {
public int count ;
public Bar() {
count = 0;
}
}
public class Foo extends Bar {
public Foo() {
count = 1
}
}
使用此代码,您可以轻松地利用多态性来遍历 List<Bar>
并收集 count
,而无需强制转换,也无需为每个子类定义 getter Bar
.
Access to fields 在编译时解析。没有像 fields overriding 这样的概念,只有 fields shadowing: The count field in Foo
隐藏 Bar
中继承的 count
字段。
为了避免阴影和混淆,您应该将继承的 class 中的字段重命名为 fooCount
例如。
不过,如果你想打印在Foo
中定义的count
字段,你可以这样做:
for (Bar b : l) {
if (b instanceof Foo) {
System.out.println(((Foo)b).count.toString()); // This always gives 1.
} else {
System.out.println(b.count.toString()); // This always gives 0.
}
}
当然,正如其他答案所建议的那样,你应该重新考虑这个设计是否好(第一个可能不好的信号是 instanceof
运算符的使用),并使用多态性来实现以更具可读性和可维护性的方式实现相同的目标。
假设我有一个摘要 parent class:
public abstract class Bar {
public int count = 0;
}
现在我有几个具体的子class。假设一个是:
public class Foo extends Bar {
public int count = 1;
}
现在我想遍历扩展抽象 class 的 children 集合。但是,当我询问它们的 count
值时,我发现我得到的是 parent 的初始化值,而不是 child 中初始化的值:
List<Bar> l = new ArrayList<Bar>();
l.add(new Foo());
for (Bar b : l) {
System.out.println(b.count.toString()); // This always gives 0.
}
我显然用错了 class 模型。正确的方法是什么,以便我可以获得由 subclasses 初始化的值?
您的迭代引用类型 Bar
,作为您的 List
.
因此它将获得 Bar
的 count
属性,被 Foo
的 count
隐藏。
这就是为什么你得到 0
。
如果您想要动态变量解析,请实现并覆盖返回 count
的方法。
如果您还使用更合适的访问修饰符设置 count
,这反过来将有助于您的封装设计。
最后的笔记
引用您的评论:
I prefer sticking to fields, because I have a lot of these fields
为您的字段实现 getter 和 setter 应该是微不足道的。
事实上,一旦您声明了您的字段,您只需按照任何受人尊敬的 IDE 中的快速向导,即可获得自动草稿或完全实施的方法。
作为一般规则,如果您的字段是可变的,您可以考虑不按原样使用模板吸气剂。
作为另一条一般规则,如果您的字段在设置前需要验证,您可以考虑不使用按原样实现的模板设置器。
我建议您仔细研究 encapsulation 的概念。
作为 Mena 回答的补充,我想指出在子类中隐藏超类属性通常是一团糟,显然不是一个好的面向对象设计。
如果您希望 Foo
和 Bar
对象具有不同的值,为什么不使用构造函数以不同方式初始化 count
字段?这是实现您要求的最直接的方法。
public class Bar {
public int count ;
public Bar() {
count = 0;
}
}
public class Foo extends Bar {
public Foo() {
count = 1
}
}
使用此代码,您可以轻松地利用多态性来遍历 List<Bar>
并收集 count
,而无需强制转换,也无需为每个子类定义 getter Bar
.
Access to fields 在编译时解析。没有像 fields overriding 这样的概念,只有 fields shadowing: The count field in Foo
隐藏 Bar
中继承的 count
字段。
为了避免阴影和混淆,您应该将继承的 class 中的字段重命名为 fooCount
例如。
不过,如果你想打印在Foo
中定义的count
字段,你可以这样做:
for (Bar b : l) {
if (b instanceof Foo) {
System.out.println(((Foo)b).count.toString()); // This always gives 1.
} else {
System.out.println(b.count.toString()); // This always gives 0.
}
}
当然,正如其他答案所建议的那样,你应该重新考虑这个设计是否好(第一个可能不好的信号是 instanceof
运算符的使用),并使用多态性来实现以更具可读性和可维护性的方式实现相同的目标。