最终类的编译器错误实例[重复]

Posted

技术标签:

【中文标题】最终类的编译器错误实例[重复]【英文标题】:Compiler error instance of final class [duplicate] 【发布时间】:2016-04-07 07:08:01 【问题描述】:

以下代码编译正常:

interface Flyer 
class Bat  

public class App 

    public static void main(String[] args)  
        Bat b = new Bat();
        if(b instanceof Flyer) System.out.println("b is a Bird");
    

 

如果我们将Bat 类设为final,则代码无法编译:

final class Bat   

如果最终类实现Flyer,则编译正常:

final class Bat implements Flyer   

有人愿意解释这背后的逻辑吗?

【问题讨论】:

【参考方案1】:

当你创建类Batfinal时,你是说这个类不能被子类化。由于Bat 没有实现接口Flyer,因此编译器能够确定b instanceof Flyer 永远不可能是true 并引发错误。

这是在 JLS section 15.20.2 中指定的:

如果将 RelationalExpression 转换为 ReferenceType 的转换(第 15.16 节)将作为编译时错误被拒绝,则 instanceof 关系表达式同样会产生编译时错误。在这种情况下,instanceof 表达式的结果永远不会为真。

另外,来自section 15.16的关于演员表:

如果操作数的编译时类型可能永远不会根据强制转换规则(第 5.5 节)强制转换为强制转换运算符指定的类型,则会出现编译时错误。

在这种情况下,Bat 永远不能转换为 Flyer:它不会实现它,final 确保不会有子类可以实现它。


如您所见,修复方法是:

使Bat 实现Flyer:在这种情况下,instanceof 运算符将始终返回true。 删除final标识符,暗示Bat的子类可能实现Flyer

【讨论】:

无论空实例的“类型”如何,null instanceof X 都不会返回 true 吗?但是我可以看到让编译器接受这一点是非常狡猾的 @Dici 实际上,null instanceof X is always false. Ups...抱歉,你是对的,我什至用它来编写我所有的 equals 方法。我需要睡觉:D【参考方案2】:

好吧,如果Bat 是最终类并且它没有实现Flyer,那么它也不能有任何可以实现Flyer 的子类,所以instanceof 永远不会返回true。在这种情况下,编译器不允许此表达式(即 (x instanceof Y) 仅在 x 有可能包含对实现或扩展 Y 的实例的引用时才允许使用)。

在您的第二个 sn-p 中,Bat 已经实现了Flyer,因此b instanceof Flyer 将始终返回true,无论Bat 是否是最终的。

【讨论】:

我觉得很奇怪(可能是一个糟糕的设计)如果它总是false 会发生编译错误,但如果它是总是 true,那很好。 现在看来合乎逻辑...尽早检查总是一个好主意。 @MarounMaroun 这里有些不一致,我同意。 @MarounMaroun if (true)if (false) 是一样的。第一个编译得很好,第二个会发出警告。我认为合理的是,死代码比无用的检查更糟糕。如果您编写一些将始终执行的条件代码,那很好,生活比您想象的要容易,但是如果您编写一些永远不会到达的代码,您可能会对程序的行为做出错误的假设,从而造成伤害【参考方案3】:

除了来自 JLS 的 @Tunaki 的引用之外,这在 https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.5.1 中也有明确的解释,引用相关规则:

如果 S 是 final 类(第 8.1.1 节),则 S 必须实现 T,否则会发生编译时错误。

instanceof 检查遵循引用类型转换的这些规则。

【讨论】:

以上是关于最终类的编译器错误实例[重复]的主要内容,如果未能解决你的问题,请参考以下文章

TypeScript实例_手动编译与自动编译类型注解接口和类的详解

有效的最终变量与最终变量[重复]

我对多态性和类对象感到困惑[重复]

在类本身中创建一个类的实例[重复]

gcc编译器报告类的大小=零[重复]

flash里的编译器错误是啥意思啊?