Java 的‘ final’方法: 它承诺了什么?

在 Java 类中,可以将方法定义为 final,以标记此方法可能不会被重写:

public class Thingy {
public Thingy() { ... }
public int operationA() {...}
/** this method does @return That and is final. */
public final int getThat() { ...}
}

这是显而易见的,它可能有一些用途,以防止意外重写,或者可能的性能 & mdash; 但这不是我的问题。

我的问题是: 从面向对象的角度来看,我理解,通过定义一个方法 final,类设计器 我保证将始终按照描述或暗示的方法工作。但是通常这可能在类作者的影响之外,如果方法所做的比交付 财产更复杂。

句法限制对我来说很清楚,但是在面向对象的意义上有什么含义呢?大多数类作者在这个意义上是否正确地使用了 final

final方法承诺什么样的“合同”?

101684 次浏览

不,这不是课堂作者的影响。您不能在派生类中重写它,因此它将执行基类作者所希望的操作。

Http://download.oracle.com/javase/tutorial/java/iandi/final.html

值得注意的是,它建议从构造函数调用的方法应该是 final

我不确定您是否可以对“ final”的使用以及它如何影响软件的整体设计合同做出任何断言。您可以确保没有开发人员可以重写此方法并以这种方式使其契约无效。但是另一方面,最终的方法可能依赖于类或实例变量,这些变量的值由子类设置,并且可以调用其他被 覆盖的类方法。所以最终的保证最多是一个非常弱的保证。

首先,您可以标记非抽象类 final以及字段和方法。这样整个类就不能被子类化。因此,类的行为将被修正。

我同意,如果这些方法调用非 final 方法,标记方法 final并不能保证它们在子类中的行为是相同的。如果行为确实需要修正,那么这必须通过约定和精心设计来实现。不要忘了在 javadoc 中注意这一点!(java 文档)

最后但并非最不重要的,final关键字在 Java 内存模型(JMM)中具有非常重要的作用。JMM 保证,要实现 final字段的可见性,不需要适当的同步。例如:

class A implements Runnable {
final String caption = "Some caption";


void run() {
// no need to synchronize here to see proper value of final field..
System.out.println(caption);
}
}

最终方法承诺什么样的“合同”?

从另一个角度来看,任何非 final 方法都可以使用 隐性担保,您可以用自己的实现重写它,而且类仍然可以按照预期工作。当您不能保证您的类支持覆盖方法时,您应该使它成为最终的。

如前所述,final与 Java 方法一起使用,以标记该方法不能被重写(对于对象范围)或隐藏(对于静态)。这允许原始开发人员创建不能被子类更改的功能,这就是它所提供的全部保证。

这意味着,如果方法依赖于其他可定制的组件,如非公共字段/方法,那么最终方法的功能仍然可以定制。不过这很好,因为(通过多态性)它允许部分定制。

阻止某些东西可定制的原因有很多,包括:

  • 性能 ——一些编译器可以分析和优化操作,特别是没有副作用的操作。

  • 获取封装的数据 ——查看不可变对象,它们的属性在构造时设置,永远不应该更改。或从这些属性派生的计算值。JavaString类就是一个很好的例子。

  • 可靠性和契约 ——对象由原语(intchardouble等)和/或其他对象组成。当在更大的 Object 中使用这些组件时,并不是所有适用于这些组件的操作都应该适用,甚至不是逻辑操作。可以使用带有 final修饰符的方法来确保。Counter 类就是一个很好的例子。


public class Counter {
private int counter = 0;


public final int count() {
return counter++;
}


public final int reset() {
return (counter = 0);
}
}

如果 public final int count()方法不是 final,我们可以这样做:

Counter c = new Counter() {
public int count() {
super.count();
return super.count();
}
}


c.count(); // now count 2

或者像这样:

Counter c = new Counter() {
public int count() {
int lastCount = 0;
for (int i = super.count(); --i >= 0; ) {
lastCount = super.count();
}


return lastCount;
}
}


c.count(); // Now double count