简体   繁体   English

为什么声明的对象类型在运行时很重要?

[英]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? 如果DogAmimal有不同版本的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 tag as well as . 我只是注意到这个问题有标签和 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 是的,这里有几个原因

  1. Compiled class Dog was replaced by someone, from other project and new class doesn't even extend Animal. 编译类Dog被某人替换,来自其他项目,新类甚至不扩展Animal。
  2. Compiled class Dog is loaded via Dserialization and some wrong version of of Dog was packed. 编译类Dog通过Dserialization加载,并且包含了一些错误版本的Dog。

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.

相关问题 如何在运行时确定通用对象的声明类型? - How to determine the declared type of a generic object at runtime? 如果方法是通过其显式类型调用的,那么运行时性能是否重要? - Does it matter for runtime performance if a method is called by its explicit type? 为什么这个Java方法按声明的类型而不是运行时类型进行多态化? - Why is this Java method polymorphing by declared type and not runtime type? 为什么@JoinColumns 的顺序很重要? - Why does the order of @JoinColumns matter? 为什么按钮对象的行为取决于声明的位置 - Why does the button object behave differently depending on where it is declared 如何在Java运行时中获取变量的声明类型? - how to get the declared type of a variable in runtime in Java? 为什么我使用wait()/ notify()对象是什么问题,如果我只是想在线程之间发出信号? - Why does it matter what object I use wait()/notify() on, if I just want a way to signal between threads? 当您创建某种类型的对象但声明为另一种类型时,它在JAVA中意味着什么? - What does it mean in JAVA when you create an object of some type, but declared as another type? 如果使用泛型类型参数声明一个类,并且在未指定类型的情况下实例化它,它是否默认为Object? - If a class is declared with a generic type parameter, and it's instantiated without specifying the type, does it default to Object? 什么是运行时类型 object - what is the runtime type object in this
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM