关于 static final method 的疑惑

前言

在声明一个方法为 static final 时,IDEA 给出了一个 warning:

When a static method is overriden in a subclass it can still be accessed via the superclass making the final declaration not very necessary. Declaring a static method final does prevent subclasses from defining a static method with the same signature.

翻译过来大概是:将一个 static 方法声明为 final 不是非常必要的,因为即使声明为 final,这个 static 方法在子类中被 override 后,仍然可以通过父类访问这个方法。不过被声明为 final 确实可以阻止子类定义一个相同签名的 static 方法。

看来看去我还是觉得很奇怪,可能是因为英语的表达和我中文的思维不太一样?…

反正 static 真是 Java 的一个很让人迷惑的 feature =.=

还是要好好弄懂它。

static final 方法

首先,对于 static 方法,我们知道它是属于类的,而非对象,可以认为 static 方法是没有 this 隐式参数的,因此可以使用类名直接调用 static 方法,通常,在一些工具类中将方法声明为 static 使用起来会比较方便。

当然,通过对象也可以调用 static 方法,但是并不推荐这么做,因为通常一个方法被声明为 static 有两种原因:1. 这个方法不需要访问对象的状态 2. 这个方法只需要访问类的 static 域,所以如果是因为第一个原因声明的 static 方法,再用对象调用它时,容易造成混淆,因为这个对象可能和这个 static 方法毫无关系。

还有一点就是,static 方法是不能被 override 的

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

class SubClass extends SuperClass {
    public static void staticMethod() {
        System.out.println("static method in sub class");
    }

    public static void main(String[] args) {
        staticMethod();
    }
}

这时会发现可以成功的调用 staticMethod(),并且输出: static method in sub class,说明调用的是子类中这个方法,那么为什么说 static 方法是不能被 override 的呢?

看下面的改动:

class SubClass extends SuperClass {
    @Override
    public static void staticMethod() {
        System.out.println("static method in sub class");
    }

    public static void main(String[] args) {
        staticMethod();
    }
}

当我们加上 @Override 注释时就会发现编译时就报错了:SubClass.java:2: 错误: 方法不会覆盖或实现超类型的方法,这就说明在子类中的这个 staticMethod 实际上不是对父类方法的 override,而是一个普普通通的子类中的方法,仅此而已。

为什么不能 override static 方法呢?我是这样理解的,因为 static 是和类关联的,所以无关对象状态,而 override 是多态的表现,多态是针对对象而言的,因此 static 方法是不能被 override 的。

这也给我们提了个醒,想要覆盖父类方法时最好加上 @Override 注释,因为它会帮助我们鉴别是否真的 override 了父类的方法~

下面,如果我们为这个方法加上 final 呢?

class SuperClass {
    public static final void staticMethod() {
        System.out.println("static method in super class");
    }
}

class SubClass extends SuperClass {
    //@Override
    public static void staticMethod() {
        System.out.println("static method in sub class");
    }

    public static void main(String[] args) {
        staticMethod();
    }
}

这时,即使注释掉 @Override,编译也会报错,错误信息是:SubClass 中的 staticMethod() 无法覆盖 SuperClass 中的 staticMethod(),从这里就可以说明 IDEA 给出的那个 warning 的下半句了

Declaring a static method final does prevent subclasses from defining a static method with the same signature.

被声明为 final 的 static 方法的确可以阻止子类定义一个相同签名的 static 方法。

在 Stack OverFlow 有一个类似的问题

Behaviour of final static method

作者的疑问是,本来 static 方法就是不能被 override 的,为什么在父类中加了 final 修饰符之后编译器还会报错。

高票的解释是

Static methods cannot be overridden but they can be hidden. The ts() method of B is not overriding(not subject to polymorphism) the ts() of A but it will hide it. If you call ts() in B (NOT A.ts() or B.ts() … just ts()), the one of B will be called and not A. Since this is not subjected to polymorphism, the call ts() in A will never be redirected to the one in B.

The keyword final will disable the method from being hidden. So they cannot be hidden and an attempt to do so will result in a compiler error.

大概意思是: static 方法不能被 override 但是可以被 hide,子类中的 static 方法不是在 override 而是在隐藏,也就是说,如果在子类中直接调用该静态方法(不是通过类调用),那么调用的一定是子类自己的那个方法,而不是父类中的,因为子类把父类那个隐藏起来了。而 final 会阻止隐藏,所以在子类中父类的 static 方法 被隐藏 就和 final 的 阻止隐藏 冲突了,因此编译就会报错。

所以,本身在父类中声明为 static 的方法,在子类中确实不可以 override,并且子类会隐藏掉父类中的这个 static 方法,让自己的这个方法和父类的那个同名方法变成两个无关联的普通方法。如果在父类中的这个 static 方法加上了 final,那么子类中就不可以定义重名的方法了,因为子类的隐藏和 final 的阻止隐藏会发生冲突。

so,我觉得将父类的 static 方法声明为 final 还是有作用的,至少不会让子类定义一个让人迷惑的重名方法了嘛,所以最后还是取消了这个 warning 啦。

 
comments powered by Disqus