代码之家  ›  专栏  ›  技术社区  ›  brasskazoo

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

  •  36
  • brasskazoo  · 技术社区  · 16 年前

    我理解在本守则中:

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

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

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

    因福
    酒吧

    method() 作为 final 在里面 福 将禁用 酒吧 隐藏它,然后重新运行 main() 将输出:

    因福
    因福

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

    将静态方法声明为 最终的 ,如果它阻止子类有意或无意地重新定义该方法?

    this 这是一个很好的解释,说明了使用 是……)

    10 回复  |  直到 8 年前
        1
  •  40
  •   Community Mohan Dere    6 年前

    我认为记A是不好的做法。 static 方法为 final

    正如你发现的, 将防止子类隐藏该方法 .

    我对你的说法感到非常惊讶:

    因福
    因福

    在里面 Foo 将阻止 Bar

    另外,我认为人们应该经常调用 静止的 方法即使在类本身内也使用类名限定它们:

    class Foo
    {
      private static final void foo()
      {
        System.out.println("hollywood!");
      }
    
      public Foo()
      {
        foo();      // both compile
        Foo.foo();  // but I prefer this one
      }
    }
    
        2
  •  35
  •   akuhn    16 年前

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

    静态方法的问题是

    • 奇怪的是,它们是“继承”给子类的
    • 令人惊讶的是,它们不能被覆盖,只能被隐藏
    • 用一个实例作为接收方调用它们是完全不可能的

    所以你应该

    • 总是以班级为接受者给他们打电话
    • 始终使用声明类仅作为接收方调用它们
    • 始终创建它们(或声明类) 最终的

    你应该

    • 从未 以实例作为接收方调用它们
    • 使用其声明类的子类作为receiver调用它们
    • 从未

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

        3
  •  7
  •   BalusC    16 年前

    public static 方法,那么它通常已经位于所谓的 实用类 只有 static 方法。自我解释的例子有 StringUtil , SqlUtil , IOUtil ,等等。这些实用程序类是由它自己声明的 final 并提供了一个 private

    public final class SomeUtil {
    
        private SomeUtil() {
            // Hide c'tor.
        }
    
        public static SomeObject doSomething(SomeObject argument1) {
            // ...
        }
    
        public static SomeObject doSomethingElse(SomeObject argument1) {
            // ...
        }
    
    }
    

    这样您就无法覆盖它们。

    如果您的不是位于某种实用程序类中,那么我会质疑 public 修饰语。难道不是吗 私有的 ? 否则,只需将其移到某个实用程序类。不要将“普通”类与 公共静电 方法。这样,您也不需要标记它们 最终的 .

    另一种情况是一种抽象工厂类,它通过 最终的

        4
  •  4
  •   mR_fr0g    16 年前

        5
  •  3
  •   TofuBeer    16 年前

    Bar中的Test.java:8:method()无法 在Foo中重写方法();凌驾 公共静态无效方法(){

    我在编码时做了以下操作(并非总是100%,但这里没有“错误”:

    (第一套“规则”适用于大多数情况——一些特殊情况将在后面介绍)

    1. 创建一个接口
    2. 创建一个实现接口的抽象类
    3. 创建扩展抽象类的具体类
    4. 创建实现接口但不扩展抽象类的具体类
    5. 如果可能的话,请始终设置接口的所有变量/常量/参数

    由于接口不能有静态方法,因此不会出现问题。如果要在抽象类或具体类中创建静态方法,它们必须是私有的,那么就无法尝试重写它们。

    特殊情况:

    1. 将类声明为final
    2. 给它一个私有构造函数以防止意外创建

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

    1. 不需要创建接口
    2. 不需要创建抽象类

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

    示例值类是此位置类:

    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);
        }
    }
    
        6
  •  1
  •   Aditya    16 年前

        7
  •  1
  •   Gergely Szilagyi    14 年前

    大部分 final 最终的 这意味着(除其他外),VM可以内联它,避免方法调用。但情况并非如此,因为长时间(或长时间的double:P): http://java.sun.com/developer/technicalArticles/Networking/HotSpot/inlining.html .

    猜测 最终的

    只要我的两分钱。。。

        8
  •  0
  •   BillMan mulya    16 年前

    使用Spring的AOP和MVC使用final方法时,我遇到了一个不利因素。我试图在AbstractFormController中的一个方法周围使用spring的AOP,该方法被声明为final。我认为spring在类中使用bcel库进行注入,存在一些限制。

        9
  •  0
  •   Ravi Wallau    16 年前

    也就是说,我不记得遇到过这样的情况:您有一个具有公共静态方法的类,并且可以/应该扩展该类。但是,根据这里的报道,我将宣布它的静态方法为final。

        10
  •  0
  •   Aasif Ali    13 年前

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

    推荐文章