Java`final`方法:它有什么承诺?


141

在Java类中,可以将方法定义为final,以标记该方法不能被覆盖:

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

这很明显,它可能对防止意外的覆盖或性能有所帮助-但这不是我的问题。

我的问题是:从OOP的角度,我理解,通过定义一种方法final,类设计器保证该方法将始终按所描述或隐含的方式工作。但这通常可能超出了类作者的影响,如果该方法执行的操作更为复杂,则仅提供属性即可

语法约束对我来说很清楚,但是在OOP意义上有什么含义?final大多数班级作者在这种意义上是否正确使用了?

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

Answers:


155

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

这意味着,如果该方法依赖于其他可自定义的组件(例如非公共字段/方法),则最终方法的功能仍可以自定义。这样做很好,因为(具有多态性)它允许部分定制。

阻止某些内容可自定义的原因有很多,其中包括:

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

  • 获取封装的数据 -查看不可变对象,这些对象的属性在构造时设置,并且永远不要更改。或从这些属性派生的计算值。Java String类就是一个很好的例子。

  • 可靠性和合同 -对象是由基元(的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

27

最终方法应保证什么样的“合同”?

从另一个角度看,任何非最终方法都可以隐式保证您可以使用自己的实现覆盖它,并且该类仍将按预期工作。当您不能保证您的类支持覆盖方法时,应将其定型。


但是,这种观点是否暗示我最初的问题“该最终方法将始终按承诺运行”,即我不得从最终方法内部调用任何非最终方法?因为,如果我这样做了,被调用的方法可能已被覆盖,因此我不能保证最终方法的行为吗?
towi

8

首先,您可以标记非抽象类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);
  }
}  

是的,我对期末课程有所了解-很简单。关于使用JMM的最终字段的要点,不需要同步... hmm:这仅是指“指针”,对吗?我仍然可以修改它所引用的对象的不同步(好的,不是在中String,而是在用户定义的类中)。但是您所说的“最终不能保证行为” 正是我的观点。我同意,文档和设计很重要。
towi 2011年

@towi对,您final不能确保对复合对象(例如)所做更改的可见性Map
维克多·索罗金

0

我不确定您是否可以对“最终”的使用以及对软件的总体设计合同有何影响进行断言。您可以确保没有开发人员可以覆盖此方法并以这种方式使其合同无效。但是另一方面,final方法可能依赖于其值由子类设置的类或实例变量,并且可以调用其他重写的类方法。因此,最终充其量是非常薄弱的​​保证。


1
是的,那就是我的意思。对。我喜欢“弱担保”一词:-)我(主要)喜欢C ++ const。如在char const * const = "Hello"char const * const addName(char const * const name) const...
towi 2011年

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.