当前位置: 首页 > 知识库问答 >
问题:

为什么一个类能够在只有运行时错误而没有编译器错误[duplicate]的情况下强制转换一个不相关的类

司空胤
2023-03-14

这是我的代码:

public class A {
    int size = 100;
    public int getSize() {
        return size;
    }
    interface D {

    }
    class B implements D {

    }
    class C {
        int size = 999;
        public int getSize() {
            return size;
        }
    }
    public void test() {
        D d = new B();
        System.out.println(((C) d).getSize());
    }
    public static void main(String[] args) {
        A a = new A();
        a.test();
    }
}

代码编译时没有任何编译器错误。为什么它不会有编译错误呢。类C与引用类型类D和实际的类类型B没有任何关系。它是如何通过类型转换的编译器检查的?

共有2个答案

南门建章
2023-03-14

这是我的人性化解释,请参阅@Compass answer以了解规范的详细信息。。

原因是,您是从一个接口进行强制转换的,在这个接口中,它所持有的实例的实际类可能与您要强制转换的类相匹配d可以容纳类型C的实例。

只有变量类型对编译时重要,而实例的实际类型对运行时重要。

有关示例,请参阅此代码:

class B { ... }
class C { ... }
interface D { ... }

// only variable type matters for compile-time
// the compiler (usually?) doesn't care what's in them
D d = ...;
B b = ...;

// compile error
// `b` can't contain an instance inheriting from `C`.
C c2 = (C) b;

// no compile error
// `d` might contain an instance inheriting from `C`.
C c1 = (C) d;


// it's because of the possibility like below
// that the compiler doesn't complain.
class E extends C implements D { ... }

D d = new E();
C c = (C) d;
通煜祺
2023-03-14

来自JLS 5.5.1,参考类型铸造:https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.5.1

给定编译时引用类型S(源)和编译时引用类型T(目标),如果没有由于以下规则而发生编译时错误,则存在从S到T的转换。

在我们的例子中,S=D(接口),T=C(类)。

如果S是类类型:

S是一个接口,所以我们跳过这个。

如果S是接口类型:

D是一个接口,所以我们使用这个分支。

如果T是数组类型,那么S必须是java类型。伊奥。Serializable或Cloneable(由数组实现的唯一接口),或发生编译时错误。

T不是数组类型,我们跳过它。

如果T是一个不是最终的类或接口类型(§8.1.1),那么如果存在T的超类型X和S的超类型Y,使得X和Y都是可证明的不同参数化类型,并且X和Y的擦除是相同的,则会发生编译时错误。

我们没有这个。我们当然可以创建一个来实现这一点,但这需要重新定义C以适应这个模型。

否则,强制转换在编译时总是合法的(因为即使T不实现S,T的子类也可能实现)。

换句话说,编译器没有理由抛出错误,因为很可能在其他地方存在一个可用于限定强制转换的可能类。

例如,你可以在其他地方看到:

class E extends C implements D { ... }

终结C会导致编译时错误:

final class C { ... }

基本上,存在一种可能性,即存在一种在运行时工作的C实现,这种实现在编译时无法验证。

 类似资料:
  • 问题内容: 我得到这个错误 01-14 12:20:57.591:E / AndroidRuntime(1825):原因:android.content.res.Resources $ NotFoundException:字符串资源ID#0x7f040003 如果我使用类变量保存上下文并执行 context.getString(R.string.create_profile_table_sql)*

  • 问题内容: 在我的关于多态性的专业幻灯片中,我看到了这段代码,并带有一些注释: 如您所见,它在第一个强制转换语句中表示将产生 运行时 错误,而在另一个声明中将产生 编译器错误 。 是什么导致这些错误?以及它们之间有何不同? 问题答案: 仅在代码实际运行时才会发生运行时错误。这些是最困难的-导致程序崩溃和代码中的错误,难以跟踪。 一个示例可能正在尝试将字符串“ hello”转换为整数: 编译器可能不

  • 想知道这里可能发生了什么,以及如何解决这个错误?它不像taglibs库版本出现在mvnrepository站点上那样出现,所以我想知道该怎么办? 提前谢了。

  • 问题内容: 我一直在尝试创建一个新项目以使用UCanAccess读取MS Access文件。我一直在关注@Gord Thompson的信息以及github中的UCanAccess示例文件。除了我使用不同名称的事实外,每件事都是相同的。我不使用任何GUI IDE。我只是从命令行进行编译,基本上,我编写了一个执行命令行编译的Java程序。 参考文献: 在不使用ODBC的情况下从Java操作Access

  • 问题内容: 下面的代码可以快速编译和运行。 我希望这是一个编译错误,因为结构永远不能符合AnyObject。如以下通过无法编译的代码所示。 我知道某些类型(例如String)可能会发生桥接。这将转换引用类型的值类型。 在写这个问题时,我想知道投射对象的类型是什么,似乎它也以某种方式被桥接。 为什么允许这种类型的铸造?似乎期望引用类型的函数违反了约定。 在重构一些代码以支持值类型时,我偶然发现了这一

  • 我的意思是,我理解这个错误,但是我在AndroidLollipop和Marshmallow上没有看到错误,只有在奇巧上。 我通过意图将一个生物维数组从一个活动发送到另一个活动,如下所示: 在接收活动中,我正在这样做: 同样,这在Android 5和6上运行得很好,但在Android 4上却出现了错误。 从Lollipop开始的系列化发生了什么变化?