Java中final字段的继承?

12
当一个超类有一个被标记为final的字段,但一个子类覆盖(或隐藏)了这个字段时会发生什么?'final'并不能阻止这种情况发生,对吗?我正在处理的具体示例是一个Building类,不同类型的建筑物都是从中继承而来。每种类型的成本等应该对每个子类是固定的,但每种类型的建筑物应该有自己的成本。
编辑: 我后来意识到我不知道自己在上面在说什么。我真正想要的是静态变量成本(static variables of cost)。然而,如果我在超类中声明这些静态变量,它们将对超类是静态的,所以Subclass1.cost例如,指的是与Superclass.cost或Subclass2.cost相同的值。如何使变量对每个子类是静态的,而无需在每个类中声明它们呢?

类似成本这样的东西会随时间而变化,对吧?为什么要将其设为final呢?我认为隐藏一个字段不是一个好主意。 - Bhesh Gurung
3个回答

19
当应用于Java类的字段时,final关键字与继承无关。它表示在构造函数之外,该字段不能被重新赋值。 Java将名称隐藏和重写分开处理。重写实际上通过切换调用的函数在运行时更改程序的可观察行为,而名称隐藏通过更改正在引用哪个字段的静态解释来更改程序。final作为应用于重写的关键字仅适用于方法,因为Java中的字段无法进行重写。不幸的是,这些不同上下文中使用final有点令人困惑,而且没有办法防止字段在子类中被名称隐藏。
如果您希望建筑物具有不同的成本,则一种选择是在每个派生类中override一个不同的getCost方法。或者,您可以在基类中拥有单个protectedprivate字段来存储成本,然后让每个子类直接设置此字段(如果这是protected)或通过基类构造函数设置此字段(如果此字段为private)。
希望能对您有所帮助!

所以你的意思是在超类中创建一个受保护的字段,然后在子类构造函数中自动分配不同的值?听起来合理。 - TheTedinator
2
@TheTedinator - 如果您放弃字段的 final 修饰符,这将起作用。如果该字段是 final,则无法在子类构造函数中分配不同的值。相反,子类构造函数必须将值传递给超类构造函数进行分配(如我在答案中所示)。如果超类在其构造函数中未分配值,则代码将无法编译;如果子类构造函数尝试从超类设置 final 字段的值,则代码将无法编译。 - Ted Hopp
@templatetypedef,“它表示在构造函数之外,该字段不能被重新分配”并不完全正确:一个final字段根本不能被重新分配,即使在构造函数中也是如此。正因为如此,子类构造函数无法覆盖超类的final字段,就像Ted Hopp所说的那样。 - Sebastian

5
你可以通过两种方式实现这个功能:创建一个访问器函数并隐藏字段本身,或者从子类传递建筑成本到超类构造函数中。你可以将它们结合起来,创建一个不能被子类覆盖的属性:
public class Building {
    private final int cost;

    protected Building(int cost, ...) {
        this.cost = cost;
    }

    public final int getCost() {
        return cost;
    }
}

public class Cottage extends Building {
    public Cottage() {
        super(COTTAGE_COST, ...);
    }
}

2

同意其他回答,但我认为以下实现在这种情况下更好:

    public abstract class Building {

        private final int cost;

        public Building(int cost) {
            this.cost = cost;
        }

        public final int getCost() {
            return cost;
        }
    }

    class Skyscraper extends Building {
        public Skyscraper() {
            super(100500);
        }
    }

当然,该领域可以公开,但这是另一回事...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接