简体   繁体   English

Java方法关键字“final”及其使用

[英]Java method keyword “final” and its use

When I create complex type hierarchies (several levels, several types per level), I like to use the final keyword on methods implementing some interface declaration. 当我创建复杂类型层次结构(几个级别,每个级别几个类型)时,我喜欢在实现某些接口声明的方法上使用final关键字。 An example: 一个例子:

interface Garble {
  int zork();
}

interface Gnarf extends Garble {
  /**
   * This is the same as calling {@link #zblah(0)}
   */
  int zblah();
  int zblah(int defaultZblah);
}

And then 然后

abstract class AbstractGarble implements Garble {
  @Override
  public final int zork() { ... }
}

abstract class AbstractGnarf extends AbstractGarble implements Gnarf {
  // Here I absolutely want to fix the default behaviour of zblah
  // No Gnarf shouldn't be allowed to set 1 as the default, for instance
  @Override
  public final int zblah() { 
    return zblah(0);
  }

  // This method is not implemented here, but in a subclass
  @Override
  public abstract int zblah(int defaultZblah);
}

I do this for several reasons: 我这样做有几个原因:

  1. It helps me develop the type hierarchy. 它帮助我开发类型层次结构。 When I add a class to the hierarchy, it is very clear, what methods I have to implement, and what methods I may not override (in case I forgot the details about the hierarchy) 当我向层次结构添加一个类时,非常清楚,我必须实现哪些方法,以及我可能不会覆盖哪些方法(如果我忘记了有关层次结构的详细信息)
  2. I think overriding concrete stuff is bad according to design principles and patterns, such as the template method pattern. 我认为根据设计原则和模式,例如template method模式,压倒具体的东西是不好的。 I don't want other developers or my users do it. 我不希望其他开发人员或我的用户这样做。

So the final keyword works perfectly for me. 所以final关键字对我来说非常合适。 My question is: 我的问题是:

Why is it used so rarely in the wild? 为什么它在野外很少使用? Can you show me some examples / reasons where final (in a similar case to mine) would be very bad? 你能告诉我一些例子/原因, final (在类似情况下我的)会非常糟糕吗?

Why is it used so rarely in the wild? 为什么它在野外很少使用?

Because you should write one more word to make variable/method final 因为你应该再写一个单词来使变量/方法最终

Can you show me some examples / reasons where final (in a similar case to mine) would be very bad? 你能告诉我一些例子/原因,最终(在类似情况下我的)会非常糟糕吗?

Usually I see such examples in 3d part libraries. 通常我会在3d零件库中看到这样的例子。 In some cases I want to extend some class and change some behavior. 在某些情况下,我想扩展一些类并改变一些行为。 Especially it is dangerous in non open-source libraries without interface/implementation separation. 特别是在没有接口/实现分离的非开源库中它是危险的。

I always use final when I write an abstract class and want to make it clear which methods are fixed. 当我编写一个抽象类时,我总是使用final ,并希望明确哪些方法是固定的。 I think this is the most important function of this keyword. 我认为这是此关键字最重要的功能。

But when you're not expecting a class to be extended anyway, why the fuss? 但是当你不期待一个课程延长时,为什么大惊小怪呢? Of course if you're writing a library for someone else, you try to safeguard it as much as you can but when you're writing "end user code", there is a point where trying to make your code foolproof will only serve to annoy the maintenance developers who will try to figure out how to work around the maze you had built. 当然,如果你正在为别人写一个图书馆,你会尽可能地保护它,但是当你写“最终用户代码”时,有一点是试图让你的代码万无一失只能用于惹恼维护开发人员,他们将试图弄清楚如何解决你构建的迷宫。

The same goes to making classes final. 使课程最终成功也是如此。 Although some classes should by their very nature be final, all too often a short-sighted developer will simply mark all the leaf classes in the inheirance tree as final . 虽然有些类本质上应该是最终的,但是短视的开发人员往往只是将inheirance树中的所有叶类标记为final

After all, coding serves two distinct purposes: to give instructions to the computer and to pass information to other developers reading the code. 毕竟,编码有两个不同的目的:向计算机发出指令并将信息传递给阅读代码的其他开发人员。 The second one is ignored most of the time, even though it's almost as important as making your code work. 第二个在大多数时候被忽略,尽管它几乎和使代码一样重要。 Putting in unnecessary final keywords is a good example of this: it doesn't change the way the code behaves, so its sole purpose should be communication. 放入不必要的final关键字就是一个很好的例子:它不会改变代码的行为方式,因此它的唯一目的应该是通信。 But what do you communicate? 但是你在沟通什么? If you mark a method as final , a maintainer will assume you'd had a good readon to do so. 如果你把方法标记为final ,那么维护者会假设你有一个很好的读取方法。 If it turns out that you hadn't, all you achieved was to confuse others. 如果事实证明你没有,你所取得的成就就是混淆别人。

My approach is (and I may be utterly wrong here obviously): don't write anything down unless it changes the way your code works or conveys useful information. 我的方法是(我明显可能完全错了):除非改变代码的工作方式或传达有用的信息,否则不要写下任何东西。

I think it is not commonly used for two reasons: 我认为它并不常用,原因有两个:

  1. People don't know it exists 人们不知道它存在
  2. People are not in the habit of thinking about it when they build a method. 人们在构建方法时不习惯思考它。

I typically fall into the second reason. 我通常属于第二个原因。 I do override concrete methods on a somewhat common basis. 我在一些常见的基础上覆盖了具体的方法。 In some cases this is bad, but there are many times it doesn't conflict with design principles and in fact might be the best solution. 在某些情况下这很糟糕,但有很多时候它与设计原则没有冲突,事实上可能是最好的解决方案。 Therefore when I am implementing an interface, I typically don't think deeply enough at each method to decide if a final keyword would be useful. 因此,当我实现一个接口时,我通常不会在每个方法上深入思考,以确定最终关键字是否有用。 Especially since I work on a lot of business applications that change frequently. 特别是因为我在很多经常变化的业务应用程序上工作。

Why is it used so rarely in the wild? 为什么它在野外很少使用?

That doesn't match my experience. 这与我的经历不符。 I see it used very frequently in all kinds of libraries. 我看到它在各种库中经常使用。 Just one (random) example: Look at the abstract classes in: 只有一个(随机)示例:查看以下中的抽象类:

http://code.google.com/p/guava-libraries/ http://code.google.com/p/guava-libraries/

, eg com.google.common.collect.AbstractIterator. ,例如com.google.common.collect.AbstractIterator。 peek() , hasNext() , next() and endOfData() are final, leaving just computeNext() to the implementor. peek()hasNext()next()endOfData()是最终的,只将computeNext()留给实现者。 This is a very common example IMO. 这是一个非常常见的IMO示例。

The main reason against using final is to allow implementors to change an algorithm - you mentioned the "template method" pattern: It can still make sense to modify a template method, or to enhance it with some pre-/post actions (without spamming the entire class with dozens of pre-/post-hooks). 反对使用final主要原因是允许实现者更改算法 - 您提到了“模板方法”模式:修改模板方法仍然有意义,或者通过一些前/后操作来增强它(没有发送垃圾邮件整个班级有几十个前/后挂钩)。

The main reason pro using final is to avoid accidental implementation mistakes, or when the method relies on internals of the class which aren't specified (and thus may change in the future). 使用final主要原因是避免意外实现错误,或者该方法依赖于未指定的类的内部(因此将来可能会更改)。

Why is it used so rarely in the wild? 为什么它在野外很少使用?

Because it should not be necessary. 因为它不应该是必要的。 It also does not fully close down the implementation, so in effect it might give you a false sense of security. 它也没有完全关闭实现,所以实际上它可能会给你一种错误的安全感。

It should not be necessary due to the Liskov substitution principle . 由于Liskov替代原则,它不应该是必要的。 The method has a contract and in a correctly designed inheritance diagram that contract is fullfilled (otherwise it's a bug). 该方法具有契约,并且在正确设计的继承图中,合同已满(否则它是一个错误)。 Example: 例:

interface Animal {
    void bark();
}

abstract class AbstractAnimal implements Animal{
   final void bark() {
       playSound("whoof.wav"); // you were thinking about a dog, weren't you?
   }
}

class Dog extends AbstractAnimal {
  // ok
}

class Cat extends AbstractAnimal() {
  // oops - no barking allowed!
}

By not allowing a subclass to do the right thing (for it) you might introduce a bug. 通过不允许子类做正确的事(对它来说),你可能会引入一个bug。 Or you might require another developer to put an inheritance tree of your Garble interface right beside yours because your final method does not allow it to do what it should do. 或者你可能需要另一个开发人员将Garble接口的继承树放在你的旁边,因为你的最终方法不允许它做它应该做的事情。

The false sense of security is typical of a non-static final method. 虚假的安全感是非静态最终方法的典型特征。 A static method should not use state from the instance (it cannot). 静态方法不应该使用实例中的状态(它不能)。 A non-static method probably does. 非静态方法可能会这样做。 Your final (non-static) method probably does too, but it does not own the instance variables - they can be different than expected. 您的最终(非静态)方法可能也会这样做,但它不拥有实例变量 - 它们可能与预期不同。 So you add a burden on the developer of the class inheriting form AbstractGarble - to ensure instance fields are in a state expected by your implementation at any point in time. 因此,您继承了继承AbstractGarble形式的类的开发人员的负担 - 以确保实例字段处于在任何时间点实现的预期状态。 Without giving the developer a way to prepare the state before calling your method as in: 在调用方法之前,不给开发人员一种准备状态的方法,如:

int zblah() {
    prepareState();
    return super.zblah();
}

In my opinion you should not close an implementation in such a fashion unless you have a very good reason. 在我看来,除非你有充分的理由,否则你不应该以这种方式关闭实现。 If you document your method contract and provide a junit test you should be able to trust other developers. 如果您记录方法合同并提供junit测试,那么您应该能够信任其他开发人员。 Using the Junit test they can actually verify the Liskov substitution principle . 使用Junit测试,他们实际上可以验证Liskov替换原则

As a side note, I do occasionally close a method. 作为旁注,我偶尔会关闭一种方法。 Especially if it's on the boundary part of a framework. 特别是如果它在框架的边界部分。 My method does some bookkeeping and then continues to an abstract method to be implemented by someone else: 我的方法做了一些簿记,然后继续抽象方法由其他人实现:

final boolean login() {
    bookkeeping();
    return doLogin();
}
abstract boolean doLogin();

That way no-one forgets to do the bookkeeping but they can provide a custom login. 这样就没有人忘记进行簿记,但他们可以提供自定义登录。 Whether you like such a setup is of course up to you :) 您是否喜欢这样的设置当然取决于您:)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM