繁体   English   中英

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

[英]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不能忽视对实际对象类型的强制转换和调用方法,在这种情况下是动物?

这在语义上是错误的。 如果DogAmimal有不同版本的Bark方法怎么办?

假设你的基类中有一个非虚方法Bark 当你打电话给Bark on Dog它应该叫Dog.Bark ; 类似地,当你在Animal上调用Bark ,它应该调用Animal.Bark

如果编译器忽视了你所说的强制转换,它最终会调用错误的方法。

请注意,在C#中,默认情况下,所有方法都是非虚拟的,而不是java。 所以这个论点在C#的上下文中更有意义。

当然,鸭子打字可以在Java中运行。 很多东西都可以在Java中运行,但这就是它的设计方式。 声明的类型非常重要,因为Java是静态类型而不是其他东西。

静态类型不是编译时间。 当然,其中很大一部分是验证代码是否可以正确运行。 但这还不够。 由于并不总是可以验证类型总是正确的(例如将Object参数转换为特定类),因此还需要进行运行时类型检查。 如果它是static ,在编译时和duck在运行时,就可能造成非常混乱,难以调试的情况。

我只是注意到这个问题有标签和 我的回答只涉及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

是的,这里有几个原因

  1. 编译类Dog被某人替换,来自其他项目,新类甚至不扩展Animal。
  2. 编译类Dog通过Dserialization加载,并且包含了一些错误版本的Dog。

线程“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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM