[英]Why does the declared type of an object matter at runtime?
在Java这样的静态类型语言中,从我所学到的,类型声明主要用于编译时捕获错误,这是一种明显优于动态类型语言的优势。 但是看看Java进行后期绑定的时候,我们得到类似ClassCastException
错误,显示声明的类型在运行时是如何相关的。 但为什么声明的类型实际上很重要?
例如:
public class TestClass
{
public static void main(String[] args)
{
Animal d = new Animal();
((Dog)d).bark(); //ClassCastException because an Animal is not a dog, which would make sense to throw at compile-time, but not at runtime.
}
}
class Dog extends Animal{}
class Animal
{
void bark()
{
System.out.println("Woof");
}
}
我知道这是一个非常糟糕的例子,因为它是一个无人问津的演员,但我只是举了一个例子。 我们尽量避免运行时异常的时刻,所以为什么不是Java不顾演员和调用方法bark
实际的对象类型,在这种情况下是Animal
? 我一直在阅读有关鸭子打字的内容,看起来这种类似的改编可能适用于Java(即如果这个对象低音,那就让它像狗一样对待!)或任何静态类型的语言,因为在运行时,Java似乎动态行动。
编辑:现在我正在考虑更多,这个问题是关于运行时类型检查的需要。 为什么需要发生? 静态类型语言在可以忽略的转换上暂停运行时异常似乎很糟糕。
为什么Java不能忽视对实际对象类型的强制转换和调用方法,在这种情况下是动物?
这在语义上是错误的。 如果Dog
和Amimal
有不同版本的Bark
方法怎么办?
假设你的基类中有一个非虚方法Bark
。 当你打电话给Bark
on Dog
它应该叫Dog.Bark
; 类似地,当你在Animal
上调用Bark
,它应该调用Animal.Bark
。
如果编译器忽视了你所说的强制转换,它最终会调用错误的方法。
请注意,在C#中,默认情况下,所有方法都是非虚拟的,而不是java。 所以这个论点在C#的上下文中更有意义。
当然,鸭子打字可以在Java中运行。 很多东西都可以在Java中运行,但这就是它的设计方式。 声明的类型非常重要,因为Java是静态类型而不是其他东西。
静态类型不是编译时间。 当然,其中很大一部分是验证代码是否可以正确运行。 但这还不够。 由于并不总是可以验证类型总是正确的(例如将Object
参数转换为特定类),因此还需要进行运行时类型检查。 如果它是static
,在编译时和duck
在运行时,就可能造成非常混乱,难以调试的情况。
我只是注意到这个问题有c#标签和java 。 我的回答只涉及Java方面,尽管有些论据适用于两者。
问题不在于声明的类型,而在于强制转换。 在一般情况下,不知道对象的运行时类型是什么,而不是声明的类型,因此使转换必须插入运行时检查( checkcast字节码)以确保它是正确的类型。 必要时,转换是一个运行时操作,当你这样做时,你告诉编译器要抛弃它自己的安全并相信你知道你在做什么。
您可以通过查看示例生成的字节码来查看:
public static void main(java.lang.String[]); Code: 0: new #2 // class Animal 3: dup 4: invokespecial #3 // Method Animal."":()V 7: astore_1 8: aload_1 9: checkcast #4 // class Dog 12: invokevirtual #5 // Method Dog.bark:()V 15: return
如果您查看JLS部分以进行投射 ,您可以看到它说:
必须将操作数表达式的类型转换为强制转换运算符明确指定的类型。
这意味着这是一个定义明确的操作。 这不是一个必要的演员并不重要:你仍然需要编译器进行演员表,所以它确实如此。
Can you explain why runtime type checking has to happen in a static-typed language
是的,这里有几个原因
线程“main”中的异常java.lang.ClassCastException:com.example.com.example.Animal无法转换为com.example.com.example.Test.main(Test.java)中的com.example.com.example.Dog :12)
正如您所看到的错误,编译器无法检测到类型转换错误,因为如果引用Dog是Animal对象,则向下转换“((Dog)d)”是有效的转换。 所以它抛出运行时错误而不是编译时间。
编译器不知道被引用的实际对象。 它只知道引用类型。 实际对象在运行时进入画面。 因此,编译器不会为此提供编译器错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.