匿名内部类和Final修饰符

7
根据我的理解,匿名类 总是 final 的:
JLS 15.9.5 中特别提到了这一点。
然而,当我运行下面的代码来检查它时,它显示Inner 类不是 final
    public class Test{
    static class A<T> {
    }
    public static void main(String arg[]) {
        A<Integer> obj = new A() {
        };
        if ((obj.getClass().getModifiers() & Modifier.FINAL) != 0) {
            System.out.println("It is a final  " + obj.getClass().getModifiers());
        } else {
            System.out.println("It is not final " + obj.getClass().getModifiers());
        }
    }
}

以上程序的输出是:
It is not final 0

请帮我解决疑惑,因为我无法理解这种现象。

1
没有办法(语言没有语法)对匿名类进行子类化,因此匿名类实际上是final的。 - Jesper
你不能实例化静态类的对象。你是怎么做到的? - abstractnature
@abstractnature-- 请检查代码。 - T-Bag
我认为单词“implicitly”可以被替换为“effectively”。 - MC Emperor
3个回答

3
我同意这可能被视为一个错误,因为JLS中的其他情况表现出了不同的行为。一个例子是接口:根据9.1.1.1节

每个接口都是隐式抽象的。

当运行以下内容时:

interface X {
    void foo();
}

public static void main(String arg[]) {
    System.out.println(Modifier.isAbstract(X.class.getModifiers()));
}

它返回true。

另一个例子是枚举,其中JLS指定

枚举声明隐式为final,除非它包含至少一个具有类体的枚举常量

下面的示例与其完全对齐。

enum X {

}

public static void main(String arg[]) {
    System.out.println(Modifier.isFinal(X.class.getModifiers()));  // true
}

每个接口都是隐式抽象的,但你已经将其显式化了。你最好移除 abstract 关键字。 - MC Emperor
我不同意“bug”这个术语。问题在于,“implicitly”太模糊了,而接口/枚举的情况与匿名类的情况不同。什么是final class?一个你无法派生子类的类,所以对于匿名类也是如此,没有必要将它们标记为final,两种情况都没有有趣的好处(或者说有吗?)。 - Jean-Baptiste Yunès
@Jean-BaptisteYunès 我倾向于将“implicitly”这个词理解为你不需要在代码中显式标记它,但是毕竟它是最终的,并且修饰符实现并没有反映出来,这就是我所指的“错误”。相同的单词“implicitly”在JLS的其他地方也出现过,在这些情况下反射代码会产生积极的结果。 - M A
1
@Jean-BaptisteYunès,这确实是一个错误,并解决的方法是更改规范(https://dev59.com/3lQJ5IYBdhLWcg3wW0my#54019398):“匿名类永远不会是final”。 - Holger

1
显式是指在源代码中写明的内容。所以,如果一个类被声明为public final class,那么这个类就是显式地final隐式是指在源代码中没有明确写出来的内容,但在某种结构或根据语言规则的上下文中,元素的行为就像已经用指定的修饰符声明一样。
例如,在声明enum SomeEnum { }中使用关键字enum会导致SomeEnum被视为final,因为语言规则要求如此。它的效果与关键字final的效果相同。
匿名类被隐式视为final的例子是因为不存在任何语言结构来覆盖匿名类。因此它的行为就像它是final一样。我认为这里用“有效地”更好。
然而,您不能根据反射如何呈现事物来做出假设。请考虑以下代码片段:
public class Test {
    interface SomeInterface { }
    abstract interface SomeAbstractInterface { }
    static abstract class SomeAbstractClass { }
    enum SomeEnum { }

    public static void main(String arg[]) {
        System.out.println(Modifier.toString(SomeInterface.class.getModifiers()));
        System.out.println(Modifier.toString(SomeAbstractInterface.class.getModifiers()));
        System.out.println(Modifier.toString(SomeAbstractClass.class.getModifiers()));
        System.out.println(Modifier.toString(SomeEnum.class.getModifiers()));
    }
}

结果是这样的:

abstract static interface
abstract static interface
abstract static
static final

interfaceabstract interface都被视为抽象接口。它们在反射中也被认为是静态的。显然,在解析和编译Java源代码的过程中,一些修饰符可能会被删除或添加。


实际上,反射行为与编译时行为一致,例如javac允许将匿名内部类的实例强制转换为它没有实现的接口类型,而对于final类型则不允许这样做,这表明匿名内部类实际上并不是final。如今,即使规范也反映了这一事实,“匿名类永远不会是final”。 - Holger

0
JLS 15.9.5中提到了“...隐式final...”,但这并不是指“final”,而是指你不能扩展一个匿名类。虽然没有语法结构来表示这一点,但该类并未标记为final。

我可以理解这里没有语法结构,但它应该通过最终检查,不是吗? - T-Bag
不,它是“final”因为它不能被扩展,而不是因为它被标记为“final”。因此,它没有修饰符,但无论你尝试什么,你都无法扩展一个匿名类,这意味着它在定义上是“final”的。 - AxelH

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