何时在PHP中使用Final?

67

我知道什么是Final类的定义,但我想知道final真正需要的情况和时机。

<?php
final class Foo extends Bar
{
   public function()
   {
     echo 'John Doe';
   }
}

如果我理解正确,'final'使其能够扩展'Foo'。

有人可以解释一下何时以及为什么应该使用'final'吗?换句话说,有没有任何原因不应该扩展类?

例如,如果类'Bar'和类'Foo'缺少某些功能,创建一个扩展'Bar'的类会很好。


2
https://en.wikipedia.org/wiki/Composition_over_inheritance - hakre
5个回答

73

有一篇关于"何时声明类为final"的好文章。以下是其中的几句引用:

简而言之:如果一个类实现了接口,且没有定义其他公共方法,请始终将其声明为final

为什么我必须使用final

  1. 防止大量继承链
  2. 鼓励组合
  3. 强制开发人员考虑用户公共API
  4. 强制开发人员缩小对象的公共API
  5. final类始终可以被扩展
  6. extends破坏了封装性
  7. 您不需要那种灵活性
  8. 您可以自由更改代码

何时避免使用final

final类只在以下假设下有效:

  1. 有一个抽象(接口),该final类实现该接口
  2. final类的所有公共API都是该接口的一部分

如果这两个先决条件之一不满足,则您很可能会达到一个时间点,使类可以扩展,因为您的代码并未真正依赖于抽象。

P.S. 感谢@ocramius提供精彩的阅读材料!


1
当您想创建一个实用类时,请使用 final - Yousha Aleayoub

26

对于一般使用情况,我建议不要将类定义为final。可能有一些用例是有意义的:如果您设计了一个复杂的API/框架,并希望确保您框架的用户只能覆盖您想要他们控制的功能部分,则限制这种可能性并使某些基本类成为final是有意义的。

例如,如果您有一个Integer类,在该类中定义add(...)方法,那么将其定义为final可以防止您框架的用户对该方法进行重写。


6
过去常常让我感到惊讶的是,我经常需要做一些看似荒谬的事情,而这些事情只是随意地被阻止了,就像你所举的 Integer 类的例子一样。当然,每次发生这种情况都是在对代码进行数周仔细研究的基础之上,但这种情况已经发生了几次。 - Iiridayn
9
这是纯粹的观点(因此通常会导致关闭问题),但除此之外,很可能不是关于编程方面的深思熟虑的意见。请参阅 http://programmers.stackexchange.com/q/89073/24482。 - hakre
@hakre 我看到的是相反的,当前问题鼓励基于观点的回答,因此有一个标志可以用来指出这一点。 - Purefan

16

原因如下:

  1. 将类声明为final可以防止其被子类化,这是终点。

  2. 将类中的每个方法都声明为final允许创建子类,它们可以访问父类的方法,但不能覆盖它们。子类可以定义自己的附加方法。

  3. final关键字仅控制覆盖的能力,不应与private可见性修饰符混淆。私有方法无法被任何其他类访问,而final方法可以。

—— 引自 David Powers 的书籍《PHP面向对象解决方案》第68页。

例如:

final childClassname extends ParentsClassname {
    // class definition omitted
}

这涵盖了整个类,包括它的所有方法和属性。任何尝试从子类名(childClassname)创建子类现在都会导致致命错误。 但是,如果您需要允许该类被子类化,但又需要防止特定方法被覆盖,那么final关键字应放在方法定义之前。

class childClassname extends parentClassname { 
    protected $numPages;

    public function __construct($autor, $pages) {
        $this->_autor = $autor;
        $this->numPages = $pages;
    }

    final public function PageCount() { 
        return $this->numPages; 
    }
}

在这个例子中,它们中的任何一个都无法覆盖PageCount()方法。


7

抱歉,但那不是我的问题。 - Inga Johansson
抱歉,我在提交按钮上有些冲动了。已编辑。如果您希望我扩展,请告诉我。 - Matt Asbury
2
是的,"如果我理解正确,'final' 使其能够 扩展 'Foo'。" - Malfist
1
@Gumbo,我只是想知道创建Final类是否是一种不好的做法,而PHP手册并没有告诉我。你能告诉我在哪里可以找到这个信息吗? - Inga Johansson
@Inga Johansson:我更倾向于回复@matt_asbury。 - Gumbo
显示剩余3条评论

5

我的观点:

何时使用final

  • 永远不要使用!

为什么?

  • 它会破坏在单元测试中使用测试替身的能力。
  • 可能会导致下游代码功能间隙而增加代码重复。
  • 使用它的原因都是通过激进的捷径解决的培训问题。

使用它的不好的原因:

  • 防止大量继承链的末日 (培训问题)
  • 鼓励组合 (培训问题)
  • 强制开发人员考虑用户公共API (培训问题)
  • 强制开发人员缩小对象的公共API (培训问题?代码审查?)
  • 一个final类总是可以被扩展 (相关性?)
  • extends破坏封装 (什么?不良的封装破坏封装;不是继承。继承本质上并不是邪恶的。)
  • 你不需要那种灵活性 (短视开发的典型思维。要准备好碰到功能壁垒和培训问题)
  • 你可以自由地更改代码 (相关性?)

5
尊敬的人士,我非常强烈地不同意这一观点。首先,这不是一个“培训问题”——有时候使用您的类的人不是您的责任,比如如果您正在编写一个库,但您仍然希望阻止他们在继承方面自食其果。模拟问题可以通过让该类实现一个接口并对其进行模拟来解决。如果需要重用某些功能,则可以将其提取到子类或特性中,如果需要复制大量代码以重新实现功能,则该类可能太大了。 - Matthew Daly
1
抱歉,重构被称为提取类(Extract class),而不是提取子类(extract subclass)。 - Matthew Daly
2
“培训问题”意味着这并不是一个编码问题,而是一个人员问题。与人交流,记录得更好,经常进行设计讨论。 - John Brown
2
完全同意这个答案。我已经厌倦了在php项目中看到每个类上的“final”关键字。似乎有些开发人员甚至自豪地说,在他们的IDE中,他们的类模板文件中有“final”关键字。这太可怕了。C++也引入了“final”关键字,你已经可以看到一些视频谈论如何在使用它时要非常小心。 - AntonioCS
3
所以,这是一个旧的帖子了。除了上面提到的所有其他观点之外,Final关键字对我来说只是太过自大了,我无法假设任何一种方法是解决软件问题的唯一和最佳方法。这是一个永久学习的旅程。因此,每当我编写代码时,我希望为下一个设计师使生活变得更加轻松,而不是更加困难,以便他们可以扩展和测试其项目的功能。除非我在客户的项目中有直接命令这样做,否则我不会使用Final。我说了我要说的话。 - John Brown
显示剩余15条评论

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