【问题标题】:'final' modifier on a class in JavaJava中类的'final'修饰符
【发布时间】:2014-01-31 13:23:05
【问题描述】:

我有一个快速而简单的问题。我习惯于让每一堂课都成为“最终”,当然,除非它需要被另一堂课扩展。

这是一个坏习惯吗?好习惯?这还重要吗? 我了解修饰符对类的影响。

提前非常感谢!

编辑: 这是一个示例代码。该类不会被任何其他类扩展。

public final class Application {

    /**
     * Starts the application.
     * 
     * @param arguments arguments provided from command-line
     */
    public static void main(String[] arguments) {
        LaunchUtilities util = new LaunchUtilities(new EventHandler());

        try {
            util.addListener(43594);
        } catch (IOException ioe) {
            Logger.getLogger(Application.class.getName()).log(Level.SEVERE, "Could not bind a port to a listener!", ioe);
        }

        util.start();
    }
}

【问题讨论】:

  • 这取决于谁将使用您的课程。
  • 有谁知道各种用于单元测试的模拟库是否荣誉“最终”?
  • @Paulo 我正在为游戏编写服务器,我不打算向公众发布代码,所以我是唯一使用代码的人。
  • 如果您尝试扩展 final 类而没有意识到您正在扩展它,这可能会很糟糕。
  • @fireshadow52 我没有将“final”修饰符放在我知道需要扩展的类上。

标签: java class extend final


【解决方案1】:

程序员(甚至 Java 专家)对此持不同意见。

Josh Bloch 设计了 ​​Java 集合库 java.Math,assert,并且是 Google 的首席 Java 架构师(或者在他们聘请 Gosling 之前),他的“Effective Java”一书中有一节专门讨论这个问题.我同意他要说的话:

第 17 项:设计和记录继承或禁止继承

他指出,没有为它设计的子类通常会导致灾难。

此外,为继承而设计的成本很高。

  1. 它极大地限制了您的班级可以做什么
  2. 您必须编写更多文档,以便子类作者知道类内部如何使用公共方法
  3. 您必须对其进行测试。这需要测试编写子类

您总是可以改变主意,使某些事情无法最终确定。你不能把过去不是最终的东西做成最终的。

阅读“Effective Java”会让这个论点更有说服力。它还将使您成为更好的程序员。

【讨论】:

    【解决方案2】:

    我要说坏习惯,原因如下:

    • 您没有指定任何特定的类需要是最终的。
    • 您违反了open/closed principle。类应该对扩展开放,对修改关闭。
    • 最终的类可能难以使用模拟框架进行测试。

    例如:

    public static void main(String[] args) {
        final Fruit mockFruit = Mockito.mock(Fruit.class);
    }
    
    private static final class Fruit {
    
    }
    

    ...将产生...

    Exception in thread "main" org.mockito.exceptions.base.MockitoException: 
    Cannot mock/spy class org.foo.Foo$Fruit
    Mockito cannot mock/spy following:
      - final classes
      - anonymous classes
      - primitive types
    

    当然,完成类有一些有效的场景。例如,您的类是不可变的。

    【讨论】:

    • 我认为你不应该嘲笑实现类。只有为继承而设计的类。如果您可以使用 mock 对其进行测试,那么将您的代码与特定实现绑定是没有意义的。
    • 扩展与子类化也不是一回事。每个公共类都可以以基于组合的方式使用,但“继承违反了封装”,因为如果你重写一个方法,它可以改变其他方法的行为,所以你必须为继承进行设计。见stackoverflow.com/questions/657987/…
    【解决方案3】:

    这是一个好习惯。将最终类更改为非最终类不应破坏任何代码。将非最终类更改为最终类可能会破坏某些代码。

    【讨论】:

    • 除非您正在编写 API 代码,并且 API 用户无法修改您的源代码。
    • @Steve Inheritance 被高估了。 java.dzone.com/articles/inheritance-overrated-needed。 API 用户应该使用组合。
    • 来自 Effective Java“设计和文档继承或禁止它”
    【解决方案4】:

    我会说这是一个坏习惯,因为这意味着你没有仔细考虑这个决定。不做最终的重点是您可以继承并进行更改而不更改原始代码。你打破了这个。

    【讨论】:

    • “你没有考虑过这个决定”是什么意思?
    • 正是我所说的。您将其描述为一种习惯,这意味着您并没有做出具体的、有意识的决定,即永远不要覆盖这个类。
    • 但是,不将事情标记为最终也是在做出决定。不同之处在于,如果它是最终的,则在子类化时不需要测试/记录行为。
    【解决方案5】:

    您的问题没有准确的答案。这取决于您编写的课程的目的是什么。有些本质上应该是最终的,而另一些则不应该,除非您想明确禁止子类化。

    如果您只是自己编程,那么我认为这不会产生任何影响,每次您知道或需要子类化某些东西时,您都会删除 final 修饰符。

    总之,final 修饰类的修饰符通常是向其他人暗示应该如何使用您的类,而不是真正的好/坏习惯。

    【讨论】:

    • 补充一点很重要,类上不必要的“final”修饰符会使代码用户的生活变得相当困难。从安全的角度来看,我在嵌套类上读到“final”对发出的字节码没有影响。
    • 即使您是“独自”编程,在对非平凡代码进行单元测试时也会让您的生活变得非常艰难。
    【解决方案6】:

    它对 HotSpot 没有影响,但我不确定其他运行时系统。我从不使用它,因为它似乎没用。虽然变量中的 final (在 HotSpot 中也没有影响)可以阻止您更改您不想更改的值,但您真的不用担心类。

    不过,它曾经在 HotSpot 中很重要。这对于 Android 和 Dalvik 之类的东西可能很重要。

    【讨论】:

      【解决方案7】:

      总的来说,这不是一个好主意。如果课程仅限于您的项目,则可以这样做。当您看到需要扩展某些类时,他总是可以使它们成为非最终类。

      【讨论】:

      • 是我误会了你,还是你的第一句话和最后一句矛盾?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-12
      • 2015-04-29
      • 2015-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多