我实现了这段代码:
class A {
//some code
}
class B extends A {
// some code
}
class C {
public static void main(String []args)
{
B b1 = (B) new A();
A a1 = (B) new A();
}
}
这两行在单独编译时编译正常,但会给出运行时错误
使用 java.lang.ClassException: A cannot be cast into B
。
为什么他们编译得很好,但是给出了运行时错误?
答案 0 :(得分:8)
您需要了解,在A
类型的引用下,可以存储类A
的任何对象或扩展A
的类,就像您的B
一样。< / p>
所以有可能
A a = new B();
由于a
引用下的对象是类B
的实例,因此应该可以以某种方式将其存储在更准确的引用中,例如B b
。所以让我们试试这个:
B b = a;//Type mismatch error
此类代码给出了编译时Type mismatch
错误,以防止出现这种情况:
我们说我们有
class B1 extends A
和class B2 extends A
我们创建了A a = new B1()
。现在,如果编译器不会为Type mismatch
抛出B1 b = a
错误,那么它也应该允许B2 b = a
,但在a
下有B1
的实例,它无关与B2
。
告诉编译器我们知道潜在的类型不匹配,我们需要明确地使用强制转换为我们想要的类
B b = (B) a;
这就是将参考A
投射到编译时可能extends A
的类的原因。
在您的代码中
B b1 = (B) new A();
A a1 = (B) new A();
您需要知道new
运算符正在返回与创建对象相同类型的引用,因此new A()
将返回A
类型的引用,因此
B b1 = (B) new A();
实际上与
相同A tmp = new A();
B b1 = (B) tmp;
问题在于,您无法在其派生类的引用中存储某个类的对象。为什么?如果该派生类具有该对象类没有的新方法
,该怎么办?class A {
// some code
}
class B extends A {
private int i;
public void setI(int i){
this.i=i;
}
}
之后
B b = (B)new A();
你会尝试调用b.setI(42);
吗?会有可能吗?不,因为A类的实例没有方法setI
,即使id有,也没有在此方法中使用的字段int i
。
这就是(B)new A();
抛出java.lang.ClassCastException
的原因。
答案 1 :(得分:7)
它在运行时失败的原因是该对象不是B.它是A.所以虽然 some As可以转换为B,但你的不能。
编译器无法分析A对象发生的所有事情。例如。
A a1 = new B();
A a2 = new A();
B b1 = (B) a1; // Ok
B b2 = (B) a2; // Fails
所以编译器不确定你的A对象是否实际上可以转换为B.所以在上面,它会认为最后2行是正常的。但是当你真正运行程序时,它意识到a2
不是B,它只是A。
答案 2 :(得分:2)
A a1 = (B) new A();
因为A
不是B
。
编译时可行,因为您正在构建并明确保证在运行时{%1}}确定的编译器将为A
。
答案 3 :(得分:2)
它自己暗示compiler
的名称只会查看expression
的编译时类型。
它不会对expression
的运行时类型做出假设。
http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.5.1
来到真正的问题
你不能将A投射到B.你可以投B到A.当你有芒果时,你有水果。但是当你有水果时,它不是意味着你有一个芒果。
答案 4 :(得分:2)
当B扩展A时,意味着A的所有方法和属性也存在于B中。
所以你可以将B投射到A,
但是你不能把A投到B。
你必须真正关心在你的应用程序中进行投射。
答案 5 :(得分:2)
当你说B延伸A时,A成为B的父亲 从技术上讲,B具有A 加上自己的的所有特征 而A只有自己的特征
如果你说将A转换为B并分配给B,那就没问题 但是如果你说把A加成B并分配给A,这是不可能的,因为A在这里不知道B中存在的额外的特征。
并且这些事情在运行时发生,因此它会给你一个运行时错误。
答案 6 :(得分:1)
我不确定编译部分,但我可以解释运行时错误。
B扩展A,这意味着B类的每个对象也是A类型的对象。反之则不然。
将A与'哺乳动物'进行比较,将B与'牛'进行比较。奶牛总是哺乳动物,但不是每个哺乳动物都是奶牛。
答案 7 :(得分:1)
与铸造完成时有关。你告诉编译器:“嘿,不要担心,这就是我说的,如果你遇到问题,请在运行时随身携带。”
基本上,编译器让你做你的事情。当您显式转换某些内容时,编译器不会执行检查。当你运行,并且程序尝试强制转换但失败时,就会看到错误。
答案 8 :(得分:1)
以下是编译时强制转换 -
A a = new B();
这样的静态转换是由编译器隐式执行的,因为编译器知道B是-A的事实。
以下不编译 -
B b = new A();
此处没有编译时转换,因为编译器知道A不是B.
以下编译 -
B b = (B) new A();
这是一个动态的演员。使用(B)
,您明确告诉编译器您希望在运行时进行转换。并且你在运行时获得一个CCE,当运行时尝试执行转换但发现它无法完成并抛出一个CCE。
当你做(或必须做)这样的事情时,责任落在你(而不是编译器)上,以确保在运行时不会发生CCE。
答案 9 :(得分:0)
这很简单。认为在扩展时必须使用is a
B `is a` A
A `is not` B
更现实的例子
class Animal{
}
class Dog extends Animal{
}
class Cat extends Animal{
}
DOG IS A
动物
动物IS NOT
需要一只狗(例如:猫不是狗,猫是动物)
你得到runtime exception
原因,在运行时意识到那只动物不是狗,这就叫downcasting
并且不安全你想做什么。
答案 10 :(得分:0)
B extends A
意味着B
A
,即B就像A,但可能会添加其他内容。
相反的是错误的。 A
不是 B
。因此,您无法将A
投射到B
。
这样想。 A
是Animal
。 B
是Bee
。是Bee
动物吗?是的。有动物蜜蜂吗?不是不是。例如,狗是动物,但绝对不是蜜蜂。
答案 11 :(得分:0)
因为A
是B
的父级。 B
扩展 A
的功能,但保留A
的原始功能。因此,B
实际上可以转换为A
,但反之亦然。
如果B
添加了新方法,请说newMethodInB()
。如果你试图通过实例B
上的变量A
调用该方法(想象一下演员的工作),你会期待什么?好吧,你肯定会收到错误,因为A
中不存在该方法。
答案 12 :(得分:0)
因为,所有编译器都看到A被强制转换为B.因为某些A实际上可以是B,这可能适用于那些A.通过编写显式强制转换,您可以确保此特定A实际上是有效的B.但是,情况并非如此。
A justA = new A();
A anAThatIsAlsoAValidB = new B(); // implicit cast to supertype
B b1 = (A) anAThatIsAlsoAValidB ; // Cast an A into a B. At runtime, this will work fine! Compiler allows casting A into B.
B b2 = (A) justA; // Cast an A into a B. At runtime, this won't work. Compiler has/uses no more info than above.
这就是为什么编译器并不真正了解类型:
com.example.ThridPartyType obj = new com.example.ThridPartyType();
B b = (B) obj.getSomeA();
// getSomeA() returns A and that is all the compiler knows.
// Depeding on the implementation of "ThridPartyType::getSomeA()" the A returned may or may not actually also be a valid B.
// Hence, if the cast works or not will only be known at runtime. If it doesn't, the Exception is thrown.