当我在以下代码中声明数组时,我无法理解实际发生的情况。
class Type1 {
}
class Type2 extends Type1 {
public void method2() {
}
}
class Test {
public static void main(String[] args) {
Type1[] x = new Type2[2];
x[0] = new Type1(); // runtime error
x[1] = new Type2();
x[1].method2(); // syntax error
}
}
我认为,因为数组声明的右侧是new Type2[2]
,所以数组将由Type2
类型的引用变量组成。如果这是真的,那么第一个错误是有意义的,因为我不能有一个引用超类型的子类型。
但是,为什么第二个错误会在此之后出现两行? Type2不知道method2()
,所以引用变量知道该方法吗?这似乎是因为Type1
不知道method2
,所以这是否意味着数组由Type1
类型的引用变量组成?如果这是真的,为什么第一个错误发生,因为它不再是一个引用超类型的子类型?
另外,为什么第一个错误是运行时错误而另一个是语法错误?
请注意我只是在我的第二个编程课程中,所以我的术语可能有点偏差。
编辑:问题here没有回答我的问题,因为它没有回答为什么像x
这样的数组元素无法调用method2()
,即使x
的元素} Type 2
。我的问题因此而有所不同,因为我的问题也询问为什么第二个错误也会发生时出现第一个错误(为什么x
的元素不能引用类型Type1
的对象和同一时间无法调用method2()
)。我原本以为如果发生一个错误,那么另一个就不会发生。我想比较两个错误和更深入的解释,而不仅仅是多态的规则。
答案 0 :(得分:15)
这是Java允许您做的一些奇怪的事情,将派生类的数组分配给基类数组的变量。
在您的代码中,编译时x
的类型为Type1[]
。那就是编译器认为的那样。在运行时,x
的类型为Type2[]
,但编译器不知道。
第一个错误发生在运行时,因为正如您所说,您无法将Type1
分配给Type2
类型的变量。
但第二个错误发生在编译时,因为编译器仍然认为x
的类型为Type1
,并且method2
中没有名为Type1
的方法,甚至虽然x
实际上在运行时持有Type2[]
。
要调用method2
,您需要通过强制转换告诉编译器x[1]
类型为Type2
:
((Type2)x[1]).method2();
每天的课程?不要这样做:
Superclass[] array = new Subclass[2];
你会让自己陷入困境。
答案 1 :(得分:6)
超类型引用可以引用子类型。我想你明白这一点,因为第一个错误与你有关。
第二个错误源于这样一个事实:在编译时,x
仍然是Type1[]
。这意味着在运行时,引用可以包含任何子类型,包括不具有方法method2
的类型。这就是为什么您只能使用Type1
中定义的方法。
例如,您可以使用Type2
在运行时检查该类型是否为isInstanceOf
,然后将其转换为Type2
,然后使用method2
。
但是,通常有更好的解决方案。
答案 2 :(得分:0)
将其用于以下代码的外行术语,
Type1[] x = new Type2[2];
让我们假设Type1 x = new Type2;
不是数组,而只是类。
您从上述步骤推断出什么?
这里x是参考变量。我们正在创建一个" Type2"并且x具有Object Type2的引用 [简单地说,x是指向Type2对象的远程控制] 。别忘了,x属于' Type1',这意味着它没有' method2()'遥控器上的按钮。[记住,方法2是Type2 Class而不是Type1]。
编译器调用对象上的方法,只有当它在类中看到它时才会调用它。编译器不关心你正在创建哪个Object [ here :new Type2],它关心的是你正在进行调用的人( here:x [1],类型为Type1 )。因此,它会抛出编译时错误。它不会打扰在运行时发生的事情或它指向的对象,它关心的是引用类型是否具有调用方法[在我们的术语中,遥控器是否具有该按钮< / EM>]吗
为什么第一个错误是运行时错误,而另一个错误是语法 错误?
我希望上面的解释(当您根据问题考虑数组类型时)回答问题的后半部分。
而且,至于第一部分,你几乎已经回答了它。无论如何,Arrays允许这样的东西在运行时爆炸,而Arraylist在编译时这样做。[这是泛型的一部分]。
正如你所说,你对这些东西不熟悉,这是我对你的参考。
参考:
Head First Java