What is the class type of the ref superclass that points to the subclass object?
I have the following codes:
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. }
Line 6 shows what the a
type is class B
. However, when it reaches the line 10, the compiler generates an error : location: variable a of type A
.
So my question is: What is the class type now a
? Why getClass()
does it show it has a type class B
but the compiler complains about it as a type a
at compile time?
Also, since it is a instanceof B
true, why can't I access valB
?
To make it clearer:
EDIT . I ran this statement: System.out.println(a);
and the result was B@36d98810
, which somehow proves that the toString()
method was executed class B
. Since the variable a
can access the toString () method in class B
, why can't it access valB
which is also in class B
?
source to share
Professor Jonathan Shevchuk of UC Berkley explains that there is shadow circulation here . Start at 18 minutes. (If the link only changes google search for CS 61B Lecture 15: More Java)
To answer the short question, there are two types of variables, static type and dynamic type.
Static type is its Type at compile time
Dynamic type is its Type at run time.
In your example
A a = new B();
The static type a is A and the dynamic type a is 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.
This is true in C # only if the method is overridden in the subclass
Update: String
a instanceof A
indicates whether the type is dynamic type of a OR subclass A
Update 2: An example to illustrate this
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");
}
}
Note that the more readable and preferred way to call a.MakeStaticSound () is Animal.MakeStaticSound ()
source to share
a
is not an object. This is a variable.
Variable type a
. The type of object for which the value of the variable refers to at run time is B
.
The compiler decides everything against the compile-time type of the expressions involved - the variable in this case. When trying to resolve the name valB
during compile time, a
it can't find anything - hence the error.
source to share
You need to keep in mind that compilation and execution are two different processes that take place at different times and have different information available to them. The compiler has to predict the future - it has to decide whether it can guarantee that your code will make sense in the future at runtime. It does this by examining the types of objects in your code. On the other hand, the runtime must check the current state .
When you read a line A a = new B()
, you output more information about the local variable a
than the compiler does. The compiler basically just sees it as A a = <some expression>
. It does not take note of the content of the expression that was used to create the value for a
.
The fact that you said A a = ...
, you are telling the compiler, "hey, this a
thing that I will be dealing with in the rest of my program is just a
, don 't think about it anymore." If you said B a = ...
, then you are telling the compiler what it is B
(and the compiler also sees B extends A
elsewhere in your code, so it knows that as well a
).
Subsequent expressions a instanceof A
, a instanceof B
, a.getClass()
and a.toString()
are legitimate from the point of view of the compiler, regardless of the type a
: the operator instanceof
and getClass()
> and toString()
are defined for all Object
s. (The compiler does not have to predict what value these expressions will produce at runtime, they just generate either true
, or false
some Class<?>
and some String
, respectively.)
But then when you come to a.valA
and a.valB
, the compiler really has to do some real work. He has to prove or guarantee that the facility a
will have a field valA
and valB
at runtime. But since you explicitly said this before, just assume that a
is a
, it cannot prove that at runtime it will have a field valB
.
Now, later, at runtime , the JVM has more information. When it evaluates a.getClass()
, it actually looks at the specific class that is "under the hood" a
and returns it. Likewise for instanceof B
- it looks at a specific class, and therefore the result of that expression is true
.
a.toString()
works the same way. At runtime, the JVM knows that the thing being referenced a
is in fact B
, so it executes the method B
toString
.
source to share
You should know the difference between an object type and an instance type. The compilation type is determined first and at runtime it does its best to keep that type safe. An instance type is the class whose object is being instantiated.
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.