指向子类对象的超类ref的类类型是什么?

时间:2014-11-27 18:42:09

标签: java class reference subclass superclass

我有以下代码:

1. public class Tester
2. {
3.      public static void main(String[] args)
4.      {
5.          A a = new B();
6.          System.out.println(a.getClass());    //Prints class B       
7.          System.out.println(a instanceof A);  //Prints true
8.          System.out.println(a instanceof B);  //Prints true
9.          System.out.println(a.valA);          //Prints 1
10.         System.out.println(a.valB);          //Compilation error
11.
12.     }
13. }
14.
15. class A
16. {
17.     int valA=1;
18. }
19.
20. class B extends A
21. {
22.     int valB=2;
23. }

第6行,它显示a的类型为class B。但是当它到达第10行时,编译器会产生错误location: variable a of type A

所以我的问题是:现在a的类类型究竟是什么?为什么getClass()显示它是class B类型,但编译器在编译期间将其注释为类型A

此外,由于a instanceof B为真,为什么我无法访问valB


让事情更清楚:

编辑:我运行了此语句:System.out.println(a);,输出为B@36d98810,以某种方式证明toString() class B方法已执行。由于变量a可以访问class B中的toString()方法,为什么它无法访问valB中的class B

5 个答案:

答案 0 :(得分:3)

加州大学伯克利分校的Jonathan Shewchuk教授解释了关于here的阴影。从18分钟开始。 (如果链接更改只是谷歌搜索CS 61B第15讲:更多Java)

简单地回答你的问题,变量,静态类型和动态类型有两种类型。

Static type is its Type at compile time
Dynamic type is its Type at run time.

在你的例子中

A a = new B();

a的静态类型是A,a的动态类型是B.

In Java a variable gets its non static methods from dynamic type
(if the method exists in both the parent and child class)
and 
its fields and static methods from the static type.

只有在子类

中重写该方法时,才能在C#中使用

更新: 这条线

a instanceof A

告诉您a的动态类型是A类型还是A的子类

更新2: 举例说明了这个

public class PlayGround {

    public static void main(String[] args) {
        Animal a = new Dog();
        System.out.print(a.name);// displays animal
        System.out.print("\r\n");
        a.MakeStaticSound();// displays static animal sound
        System.out.print("\r\n");
        a.MakeSound();// displays bow wow

    }

}

class Animal {
    public String name = "animal";

    public void MakeSound() {
        System.out.print("animal sound");
    }

    public static void MakeStaticSound() {
        System.out.print("static animal sound");
    }

}

class Dog extends Animal {
    public String name = "dog";

    public void MakeSound() {
        System.out.print("bow wow");
    }

    public static void MakeStaticSound() {
        System.out.print("static bow wow");
    }

}

请注意,调用a.MakeStaticSound()的更可读和首选的方法是Animal.MakeStaticSound()

答案 1 :(得分:2)

a不是对象。它是一个变量。

变量的类型为A。变量值在执行时引用的对象类型为B

编译器根据所涉及的表达式的编译时类型解析所有内容 - 在本例中为变量。尝试在编译时类型valB内解析名称A时,它无法找到任何内容 - 因此出错。

答案 2 :(得分:1)

您需要记住编译执行是两个 不同的进程 ,发生在 不同时间 ,并提供不同类型的信息。编译器必须预测未来 - 它必须决定它是否可以保证您的代码在将来在运行时有意义。它通过分析代码中对象的类型来实现。另一方面,运行时必须检查事物的当前状态


当您读取行A a = new B()时,您正在推断有关a局部变量的更多信息,而不是编译器。编译器基本上只将其视为A a = <some expression>。它 会记录用于生成a值的表达式的内容。

您已经说过A a = ...的事实是您告诉编译器:&#34;嘿,这个a我想要在其余部分处理的事情程序,它只是一个A,不再假设它。&#34;如果您反而说B a = ...,那么您告诉编译器它是B(并且编译器也会在代码中的其他位置看到B extends A,因此它知道它也是A)。

从编译器的角度来看,后续表达式a instanceof Aa instanceof Ba.getClass()a.toString()是合法的,无论{的类型如何{1}}:a运算符以及instanceofgetClass()方法已为所有toString()定义。 (编译器需要预测这些表达式在运行时会产生什么值,只是它们会生成 Objecttrue,有些分别为false和一些Class<?>。)

但是当你来到Stringa.valA时,编译器实际上必须做一些真正的工作。它需要证明保证 a.valB对象在运行时将具有avalA字段。但是,既然您之前已明确告诉它只假设valBa,则无法证明它在运行时会有A字段。


现在,稍后,在 执行时间 ,JVM有更多信息。当它评估 valB时,它实际上会查找具体类别&#34;&#34;&#34;&#34;&#34;&#34; a.getClass()并返回它。类似地,对于a - 它查找具体类,因此该表达式的结果为instanceof B

true的工作方式类似。在运行时,JVM知道a.toString()引用的东西实际上是a,因此它执行B的{​​{1}}方法。

答案 3 :(得分:0)

这是类继承,接口等的基本属性。 Class&#34; A&#34;没有变量&#34; valB&#34;。 如果你想使用变量&#34; valB&#34;在课堂上&#34; B&#34;或者,你应该首先演出Class&#34; A&#34;到&#34; B&#34;

尝试:

System.out.println(((B)a).valB);  

答案 4 :(得分:0)

您应该知道对象类型和实例类型之间的区别。首先是在编译类型中确定的,并且在运行时它会尽力保持该类型的安全。实例类型是实例化对象的类。

A a; //this is an object type
new B(); //this is an instance type
A a = new B(); //all together, but a is of type A, having instance of type B.