[英]Why does the declared type of an object matter at runtime?
In a statically-typed language like Java, from what I've learned, type declaration is essentially for compile-time catching of errors, an obvious advantage over dynamically typed languages. 在Java这样的静态类型语言中,从我所学到的,类型声明主要用于编译时捕获错误,这是一种明显优于动态类型语言的优势。 But looking at the times when Java does late binding, we get errors like ClassCastException
, showing how the declared types are relevant somehow at runtime. 但是看看Java进行后期绑定的时候,我们得到类似ClassCastException
错误,显示声明的类型在运行时是如何相关的。 But why does the declared type actually matter? 但为什么声明的类型实际上很重要?
For example: 例如:
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");
}
}
I know this is a super bad example because it's an unnessesary cast, but I'm just giving an example of it. 我知道这是一个非常糟糕的例子,因为它是一个无人问津的演员,但我只是举了一个例子。 We try to avoid runtime exceptions at all times, so why can't Java disregard the cast and call method bark
on the actual object type, which in this case is Animal
? 我们尽量避免运行时异常的时刻,所以为什么不是Java不顾演员和调用方法bark
实际的对象类型,在这种情况下是Animal
? I've been reading about duck typing and it seems like a similar adaption of this could work in Java (ie if this object woofs, then let's treat it like a dog!) or any statically-typed language, because at runtime, Java seems to act dynamically. 我一直在阅读有关鸭子打字的内容,看起来这种类似的改编可能适用于Java(即如果这个对象低音,那就让它像狗一样对待!)或任何静态类型的语言,因为在运行时,Java似乎动态行动。
Edit: Now that I'm thinking about it more, this question is about the need for runtime type checking. 编辑:现在我正在考虑更多,这个问题是关于运行时类型检查的需要。 Why does it need to happen? 为什么需要发生? It seems bad that static typed languages halt with a runtime exception on a cast which could be ignored. 静态类型语言在可以忽略的转换上暂停运行时异常似乎很糟糕。
why can't Java disregard the cast and call method bark on the actual object type, which in this case is Animal? 为什么Java不能忽视对实际对象类型的强制转换和调用方法,在这种情况下是动物?
That would be semantically wrong thing to do. 这在语义上是错误的。 What if Dog
and Amimal
has different versions of Bark
method? 如果Dog
和Amimal
有不同版本的Bark
方法怎么办?
Let's say you have a non virtual method Bark
in your base class. 假设你的基类中有一个非虚方法Bark
。 When you call Bark
on Dog
it should call Dog.Bark
; 当你打电话给Bark
on Dog
它应该叫Dog.Bark
; similarly when you call Bark
on Animal
it should call Animal.Bark
. 类似地,当你在Animal
上调用Bark
,它应该调用Animal.Bark
。
If the compiler disregards the cast as you said, it would end up calling the wrong method. 如果编译器忽视了你所说的强制转换,它最终会调用错误的方法。
Note that in C# by default all methods are non virtual as opposed to java. 请注意,在C#中,默认情况下,所有方法都是非虚拟的,而不是java。 So this argument makes more sense in the context of C#. 所以这个论点在C#的上下文中更有意义。
Of course duck typing could work in Java. 当然,鸭子打字可以在Java中运行。 A lot of things could work in Java, but this is the way it's designed. 很多东西都可以在Java中运行,但这就是它的设计方式。 The declared type matters exactly because Java is statically typed and not something else. 声明的类型非常重要,因为Java是静态类型而不是其他东西。
Static typing is not compile time only. 静态类型不是编译时间。 Certainly a big portion of it is to verify that the code can run correctly. 当然,其中很大一部分是验证代码是否可以正确运行。 But it's not enough. 但这还不够。 Since it's not always possible to verify that the types are always correct (such as casting an Object
parameter to a specific class), there needs to be runtime type checking as well. 由于并不总是可以验证类型总是正确的(例如将Object
参数转换为特定类),因此还需要进行运行时类型检查。 If it were static
at compile time and duck
at runtime, it could create very confusing and hard to debug situations. 如果它是static
,在编译时和duck
在运行时,就可能造成非常混乱,难以调试的情况。
I just noticed that this question has the c# tag as well as java . 我只是注意到这个问题有c#标签和java 。 My answer only covers the Java side, although some of the arguments would apply to both. 我的回答只涉及Java方面,尽管有些论据适用于两者。
The issue isn't with the declared type, but rather with the cast. 问题不在于声明的类型,而在于强制转换。 In the general case, it isn't knowable what the runtime type of an object is other than the declared type, so making a cast has to insert runtime checks (a checkcast bytecode) to ensure that it is the correct type. 在一般情况下,不知道对象的运行时类型是什么,而不是声明的类型,因此使转换必须插入运行时检查( checkcast字节码)以确保它是正确的类型。 Casting is, by necessity, a runtime operation, and when you do it you're telling the compiler to throw out it's own safety and trust that you know what you're doing. 必要时,转换是一个运行时操作,当你这样做时,你告诉编译器要抛弃它自己的安全并相信你知道你在做什么。
You can see this by looking at the bytecode generated by your example: 您可以通过查看示例生成的字节码来查看:
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
If you look at the JLS section for casting , you can see that it says: 如果您查看JLS部分以进行投射 ,您可以看到它说:
the type of the operand expression must be converted to the type explicitly named by the cast operator. 必须将操作数表达式的类型转换为强制转换运算符明确指定的类型。
Meaning this is a well-define operation. 这意味着这是一个定义明确的操作。 It doesn't matter that it's not a needed cast here: you're still requiring the compiler to do the cast, so it does. 这不是一个必要的演员并不重要:你仍然需要编译器进行演员表,所以它确实如此。
Can you explain why runtime type checking has to happen in a static-typed language
Yes, here are a few reasons 是的,这里有几个原因
Exception in thread "main" java.lang.ClassCastException: com.example.com.example.Animal cannot be cast to com.example.com.example.Dog at com.example.com.example.Test.main(Test.java:12) 线程“main”中的异常java.lang.ClassCastException:com.example.com.example.Animal无法转换为com.example.com.example.Test.main(Test.java)中的com.example.com.example.Dog :12)
As you can see error, The compiler fails to detect the type casting error, because down casting "((Dog)d)" is a valid casting if the reference Dog is a Animal object. 正如您所看到的错误,编译器无法检测到类型转换错误,因为如果引用Dog是Animal对象,则向下转换“((Dog)d)”是有效的转换。 so it throw runtime error instead compile time. 所以它抛出运行时错误而不是编译时间。
The compiler doesn't know which actual object being referenced. 编译器不知道被引用的实际对象。 It only knows the reference type. 它只知道引用类型。 The actual object comes into picture at runtime. 实际对象在运行时进入画面。 So, the compiler wouldn't give you compiler error for that. 因此,编译器不会为此提供编译器错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.