Java 声明一个最终的静态方法是一个坏主意吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1932399/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 01:33:54  来源:igfitidea点击:

Is it a bad idea to declare a final static method?

javastaticmethodsfinal

提问by brasskazoo

I understand that in this code:

我明白在这段代码中:

class Foo {
    public static void method() {
        System.out.println("in Foo");
    }
} 

class Bar extends Foo {
    public static void method() {
        System.out.println("in Bar");
    }
}

.. the static method in Bar'hides' the static method declared in Foo, as opposed to overriding it in the polymorphism sense.

.. 中的静态方法Bar“隐藏”了 中声明的静态方法Foo,而不是在多态意义上覆盖它。

class Test {
    public static void main(String[] args) {
        Foo.method();
        Bar.method();
    }
}

...will output:

...将输出:

in Foo
in Bar

在 Foo
in 酒吧

Re-defining method()as finalin Foowill disable the ability for Barto hide it, and re-running main()will output:

重新定义method()finalinFoo将禁用Bar隐藏它的能力,重新运行main()将输出:

in Foo
in Foo

在 Foo
在 Foo

(Edit: Compilation fails when you mark the method as final, and only runs again when I remove Bar.method())

编辑:当您将方法标记为时编译失败final,并且仅在我删除时再次运行Bar.method()

Is it considered bad practice to declare static methods as final, if it stops subclasses from intentionally or inadvertantly re-defining the method?

将静态方法声明为 是否被认为是不好的做法final,如果它阻止子类有意或无意地重新定义方法?

(thisis a good explanation of what the behaviour of using finalis..)

是对使用行为的一个很好的解释final..)

采纳答案by Gregory Pakosz

I don't consider it's bad practice to mark a staticmethod as final.

我不认为将static方法标记为final.

As you found out, finalwill prevent the method from being hidden by subclasses which is very good news imho.

正如您所发现的,final将防止该方法被子类隐藏,恕我直言,这是个好消息

I'm quite surprised by your statement:

我对你的陈述感到非常惊讶:

Re-defining method() as final in Foo will disable the ability for Bar to hide it, and re-running main() will output:

in Foo
in Foo

在 Foo 中将 method() 重新定义为 final 将禁用 Bar 隐藏它的能力,并且重新运行 main() 将输出:

在 Foo
在 Foo

No, marking the method as finalin Foowill prevent Barfrom compiling. At least in Eclipse I'm getting:

不,将方法标记为finalinFoo将阻止Bar编译。至少在 Eclipse 中我得到:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: Cannot override the final method from Foo

线程“main”中的异常 java.lang.Error:未解决的编译问题:无法覆盖来自 Foo 的最终方法

Also, I think people should always invoke staticmethod qualifying them with the class name even within the class itself:

另外,我认为人们应该始终调用static使用类名来限定他们的方法,即使是在类本身中:

class Foo
{
  private static final void foo()
  {
    System.out.println("hollywood!");
  }

  public Foo()
  {
    foo();      // both compile
    Foo.foo();  // but I prefer this one
  }
}

回答by mR_fr0g

Usually with utility classes - classes with only static methods - it is undesirable to use inheritence. for this reason you may want to define the class as final to prevent other classes extending it. This would negate putting final modifiers on your utility class methods.

通常对于实用程序类——只有静态方法的类——使用继承是不可取的。出于这个原因,您可能希望将该类定义为 final 以防止其他类扩展它。这将否定在您的实用程序类方法上放置 final 修饰符。

回答by Aditya

It might be a good thing to mark static methods as final, particularly if you are developing a framework that you expect others to extend. That way your users won't inadvertently end up hiding your static methods in their classes. But if you are developing a framework you might want to avoid using static methods to begin with.

将静态方法标记为 final 可能是一件好事,特别是如果您正在开发一个希望其他人扩展的框架。这样您的用户就不会无意中将您的静态方法隐藏在他们的类中。但是如果您正在开发一个框架,您可能希望避免使用静态方法开始。

回答by TofuBeer

The code does not compile:

代码不编译:

Test.java:8: method() in Bar cannot override method() in Foo; overridden method is static final public static void method() {

Test.java:8: Bar 中的 method() 不能覆盖 Foo 中的 method();覆盖的方法是 static final public static void method() {

The message is misleading since a static method can, by definition, never be overridden.

该消息具有误导性,因为根据定义,静态方法永远不会被覆盖。

I do the following when coding (not 100% all the time, but nothing here is "wrong":

我在编码时执行以下操作(不是一直 100%,但这里没有任何“错误”:

(The first set of "rules" are done for most things - some special cases are covered after)

(第一组“规则”适用于大多数事情 - 一些特殊情况会在后面介绍)

  1. create an interface
  2. create an abstract class that implements the interface
  3. create concrete classes that extend the abstract class
  4. create concrete classes that implements the interface but do not extend the abstract class
  5. always, if possible, make all variables/constants/parameters of the interface
  1. 创建一个接口
  2. 创建一个实现接口的抽象类
  3. 创建扩展抽象类的具体类
  4. 创建实现接口但不扩展抽象类的具体类
  5. 总是,如果可能,使接口的所有变量/常量/参数

Since an interface cannot have static methods you don't wind up with the issue. If you are going to make static methods in the abstract class or concrete classes they must be private, then there is no way to try to override them.

由于接口不能具有静态方法,因此您不会遇到问题。如果您要在抽象类或具体类中创建静态方法,它们必须是私有的,则无法尝试覆盖它们。

Special cases:

特别案例:

Utility classes (classes with all static methods):

实用程序类(具有所有静态方法的类):

  1. declare the class as final
  2. give it a private constructor to prevent accidental creation
  1. 将类声明为 final
  2. 给它一个私有构造函数以防止意外创建

If you want to have a static method in a concrete or abstract class that is not private you probably want to instead create a utility class instead.

如果您想在非私有的具体或抽象类中使用静态方法,您可能希望改为创建一个实用程序类。

Value classes (a class that is very specialized to essentially hold data, like java.awt.Point where it is pretty much holding x and y values):

值类(一个非常专门用来保存数据的类,比如 java.awt.Point,它几乎保存了 x 和 y 值):

  1. no need to create an interface
  2. no need to create an abstract class
  3. class should be final
  4. non-private static methods are OK, especially for construction as you may want to perform caching.
  1. 无需创建接口
  2. 无需创建抽象类
  3. 课程应该是最终的
  4. 非私有静态方法是可以的,特别是对于构造,因为您可能想要执行缓存。

If you follow the above advice you will wind up with pretty flexible code that also has fairly clean separation of responsibilities.

如果您遵循上述建议,您将得到非常灵活的代码,并且具有相当清晰的职责分离。

An example value class is this Location class:

一个示例值类是这个 Location 类:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public final class Location
    implements Comparable<Location>
{
    // should really use weak references here to help out with garbage collection
    private static final Map<Integer, Map<Integer, Location>> locations;

    private final int row;    
    private final int col;

    static
    {
        locations = new HashMap<Integer, Map<Integer, Location>>();
    }

    private Location(final int r,
                     final int c)
    {
        if(r < 0)
        {
            throw new IllegalArgumentException("r must be >= 0, was: " + r);
        }

        if(c < 0)
        {
            throw new IllegalArgumentException("c must be >= 0, was: " + c);
        }

        row = r;
        col = c;
    }

    public int getRow()
    {
        return (row);
    }

    public int getCol()
    {
        return (col);
    }

    // this ensures that only one location is created for each row/col pair... could not
    // do that if the constructor was not private.
    public static Location fromRowCol(final int row,
                                      final int col)
    {
        Location               location;
        Map<Integer, Location> forRow;

        if(row < 0)
        {
            throw new IllegalArgumentException("row must be >= 0, was: " + row);
        }

        if(col < 0)
        {
            throw new IllegalArgumentException("col must be >= 0, was: " + col);
        }

        forRow = locations.get(row);

        if(forRow == null)
        {
            forRow = new HashMap<Integer, Location>(col);
            locations.put(row, forRow);
        }

        location = forRow.get(col);

        if(location == null)
        {
            location = new Location(row, col);
            forRow.put(col, location);
        }

        return (location);
    }

    private static void ensureCapacity(final List<?> list,
                                       final int     size)
    {
        while(list.size() <= size)
        {
            list.add(null);
        }
    }

    @Override
    public int hashCode()
    {
        // should think up a better way to do this...
        return (row * col);
    }

    @Override
    public boolean equals(final Object obj)
    {
        final Location other;

        if(obj == null)
        {
            return false;
        }

        if(getClass() != obj.getClass())
        {
            return false;
        }

        other = (Location)obj;

        if(row != other.row)
        {
            return false;
        }

        if(col != other.col)
        {
            return false;
        }

        return true;
    }

    @Override
    public String toString()
    {
        return ("[" + row + ", " + col + "]");
    }

    public int compareTo(final Location other)
    {
        final int val;

        if(row == other.row)
        {
            val = col - other.col;
        }
        else
        {
            val = row - other.row;
        }

        return (val);
    }
}

回答by BillMan

I encountered one detriment to using final methods using Spring's AOP and MVC. I was trying to use spring's AOP put in security hooks around one of the methods in the AbstractFormController which was declared final. I think spring was using the bcel library for injection in classes and there was some limitation there.

我遇到了使用 Spring 的 AOP 和 MVC 使用 final 方法的一个不利因素。我试图使用 spring 的 AOP 在 AbstractFormController 中声明为 final 的方法之一周围放置安全钩子。我认为 spring 在类中使用 bcel 库进行注入,并且那里有一些限制。

回答by Ravi Wallau

When I create pure utility classes, I declare then with a private constructor so they cannot be extended. When creating normal classes, I declare my methods static if they are not using any of the class instance variables (or, in some cases, even if they were, I would pass the arguments in the method and make it static, it's easier to see what the method is doing). These methods are declared static but are also private - they are there just to avoid code duplication or to make the code easier to understand.

当我创建纯实用程序类时,我使用私有构造函数进行声明,因此它们不能被扩展。在创建普通类时,如果我的方法不使用任何类实例变量,我将它们声明为静态(或者,在某些情况下,即使它们使用了,我也会在方法中传递参数并将其设为静态,这样更容易看到该方法在做什么)。这些方法被声明为静态但也是私有的——它们只是为了避免代码重复或使代码更容易理解。

That being said, I don't remember running into the case where you have a class that has public static methods and that can/ should be extended. But, based on what was reported here, I would declare its static methods final.

话虽如此,我不记得遇到过这样的情况,您有一个具有公共静态方法并且可以/应该扩展的类。但是,根据此处报告的内容,我将声明其静态方法为 final。

回答by BalusC

If I have a public staticmethod, then it's often already located in a so-called utility classwith only staticmethods. Self-explaining examples are StringUtil, SqlUtil, IOUtil, etcetera. Those utility classes are by itselves already declared finaland supplied with a privateconstructor. E.g.

如果我有一个public static方法,那么它通常已经位于所谓的只有方法的实用程序类中static。自我解释的例子是StringUtilSqlUtilIOUtil,等等。这些实用程序类本身已经声明final并提供了private构造函数。例如

public final class SomeUtil {

    private SomeUtil() {
        // Hide c'tor.
    }

    public static SomeObject doSomething(SomeObject argument1) {
        // ...
    }

    public static SomeObject doSomethingElse(SomeObject argument1) {
        // ...
    }

}

This way you cannot override them.

这样你就不能覆盖它们。

If yours is not located in kind of an utility class, then I'd question the value of the publicmodifier. Shouldn't it be private? Else just move it out to some utility class. Do not clutter "normal" classes with public staticmethods. This way you also don't need to mark them final.

如果您的不在实用程序类中,那么我会质疑public修饰符的值。不应该private吗?否则,只需将其移出某个实用程序类即可。不要用public static方法混淆“普通”类。这样你也不需要标记它们final

Another case is a kind of abstract factory class, which returns concrete implementations of self through a public staticmethod. In such case it would perfectly make sense to mark the method final, you don't want the concrete implementations be able to override the method.

另一种情况是一种抽象工厂类,它通过一个public static方法返回 self 的具体实现。在这种情况下,标记方法是完全有意义的final,您不希望具体实现能够覆盖该方法。

回答by akuhn

Static methods are one of Java's most confusing features. Best practices are there to fix this, and making all static methods finalis one of these best practices!

静态方法是 Java 最令人困惑的特性之一。最佳实践可以解决这个问题,制作所有静态方法final是这些最佳实践之一!

The problem with static methods is that

静态方法的问题在于

  • they are not class methods, but global functions prefixed with a classname
  • it is strange that they are "inherited" to subclasses
  • it is surprising that they cannot be overridden but hidden
  • it is totally broken that they can be called with an instance as receiver
  • 它们不是类方法,而是以类名为前缀的全局函数
  • 奇怪的是它们被“继承”到子类
  • 令人惊讶的是它们不能被覆盖而是隐藏
  • 可以用一个实例作为接收器调用它们是完全坏的

therefore you should

因此你应该

  • always call them with their class as receiver
  • always call them with the declaring class only as receiver
  • always make them (or the declaring class) final
  • 总是用他们的班级作为接收者打电话给他们
  • 始终使用仅作为接收器的声明类调用它们
  • 总是使它们(或声明类) final

and you should

你应该

  • nevercall them with an instance as receiver
  • nevercall them with a subclass of their declaring class as receiver
  • neverredefine them in subclasses
  • 永远不要用实例作为接收者调用它们
  • 永远不要使用他们声明类的子类作为接收者来调用它们
  • 永远不要在子类中重新定义它们

 

 

NB: the second version of you program should fails a compilation error. I presume your IDE is hiding this fact from you!

注意:您程序的第二个版本应该会失败编译错误。我认为您的 IDE 对您隐瞒了这个事实!

回答by Gergely Szilagyi

Most of this finalissue dates back to the time when VM-s were quite dumb/conservative. Back then if you marked a method finalit meant (among other things), that the VM can inline it, avoiding method calls. That is not case since a long-long (or long double :P ) time: http://java.sun.com/developer/technicalArticles/Networking/HotSpot/inlining.html.

这个final问题的大部分时间可以追溯到 VM-s 非常愚蠢/保守的时候。那时,如果你标记了一个方法,final它意味着(除其他外),VM 可以内联它,避免方法调用。情况并非如此,因为很长(或长双 :P )时间:http: //java.sun.com/developer/technicalArticles/Networking/HotSpot/inlining.html

I guessthat Idea/Netbeans inspection warns you, because it thinks that you want to use the finalkeyword for optimization and they think that you are unaware of the fact that it is unneeded with modern VMs.

Idea/Netbeans 检查会警告您,因为它认为您想使用final关键字进行优化,而他们认为您没有意识到现代 VM 不需要它的事实。

Just my two cents...

只是我的两分钱...

回答by Aasif Ali

Because static methods are the properties of the class and they are called with the name of the class rather than of object. If we make the parent class method final as well it will not be overloaded as final methods does not allow to change its memory location but we can update the final data member at the same memory location...

因为静态方法是类的属性,并且使用类的名称而不是对象的名称调用它们。如果我们也将父类方法设为 final,它就不会被重载,因为 final 方法不允许更改其内存位置,但我们可以在同一内存位置更新最终数据成员......