3

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. }

At line 6 it shows that a is of type class B. However when it reaches line 10, the compiler produces an error: location: variable a of type A.

So my question is: What exactly is the class type of a now? Why getClass() shows that it is of type class B, yet the compiler complains it as type A during compilation?

Further more, since a instanceof B is true, why can't I access valB?


To make things clearer:

EDIT: I ran this statement: System.out.println(a); and the output was B@36d98810 which somehow proves that the toString() method of class B was executed. Since variable a can access the toString() method within class B, why can't it access valB which also resides in class B?

user3437460
  • 17,253
  • 15
  • 58
  • 106
  • The compiler only knows that `a` is of type `A`, so it can't infer that it will have a field named `valB`. – August Nov 27 '14 at 18:43
  • There is a difference between the class of a *reference* and the class of the *object* that the reference addresses. Casting can change the class of the *reference* but will never change the class of the *object*. (Note that reference casting is entirely different from scalar casting.) The compiler only knows what the class of the *reference* is. – Hot Licks Nov 27 '14 at 19:00
  • @HotLicks Do you mind giving a detailed answer below? I would like to hear more about your insights. – user3437460 Nov 27 '14 at 19:03
  • I will accept whoever's answer (and +1 )without hesitation for whom can directly address to my question above. – user3437460 Nov 27 '14 at 19:36

5 Answers5

3

Professor Jonathan Shewchuk from UC Berkley explains about shadowing over here. Start at 18 minutes. (If the link changes just google search for CS 61B Lecture 15: More Java)

To answer your question in short there are two types for a variable, 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 of a is A and the dynamic type of 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 sub class

Update: The line

a instanceof A

tells you whether the dynamic type of a is of type A OR a subclass of A

Update 2: AN example that illustrates 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");
    }

}

Please note that the more readable and preferred way to call a.MakeStaticSound() is Animal.MakeStaticSound()

developer747
  • 15,419
  • 26
  • 93
  • 147
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. – khelwood Nov 27 '14 at 22:45
  • @khelwood it's difficult to include parts of a video _here_ – rpax Nov 27 '14 at 23:20
  • 1
    @rpax Perfectly possible to include essential parts of an answer that one has learned by watching a video. – khelwood Nov 27 '14 at 23:48
  • "a variable gets its methods from dynamic type" makes it sound like `a.someMethodFromClassB()` would compile, when in fact it wouldn't. – Matt McHenry Nov 29 '14 at 16:27
  • @Matt: Please see the update. It gets the methods from its dynamic type if its defined in both the parent and child class. – developer747 Nov 30 '14 at 18:08
2

a is not an object. It's a variable.

The type of the variable is A. The type of the object that the value of the variable refers to at execution time is B.

The compiler resolves everything against the compile-time type of the expressions involved - the variable in this case. When trying to resolve the name valB within the compile-time type of A, it fails to find anything - hence the error.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • How do we determine which statements will be executed at runtime, and which statements will be executed at compile time? – user3437460 Nov 27 '14 at 18:55
  • @user3437460: everything is *executed* at runtime (well, aside from a few things like constant evaluation) but the compiler has to be able to resolve the names at compile time. – Jon Skeet Nov 27 '14 at 19:13
1

You need to keep in mind that compilation and execution are two different processes that happen at different times and have different kinds of 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 analyzing the types of the objects in your code. The runtime, on the other hand, just has to inspect the current state of things.


When you read the line A a = new B(), you are inferring more information about the a local variable than the compiler is. The compiler basically just sees this as A a = <some expression>. It does not take note of the contents of the expression that's used to produce the value for a.

The fact that you've said A a = ... is you telling the compiler: "hey, this a thing I'm going to deal with in the rest of my program, it's just an A, don't assume anything more about it." If you had instead said B a = ..., then you're telling the compiler that it's a B (and the compiler also sees B extends A elsewhere in your code, so it knows it's also an A).

The subsequent expressions a instanceof A, a instanceof B, a.getClass(), and a.toString() are legal, from the compiler's point of view, regardless of the type of a: the instanceof operator and the getClass() and toString() methods are defined for all Objects. (The compiler does not need to predict what value those expressions will produce at runtime, just that they will produce either true or false, some Class<?>, and some String, respectively.)

But then when you come to a.valA and a.valB, the compiler actually has to do some real work. It needs to prove or guarantee that the a object will have a valA and a valB field at runtime. But since you've explicitly told it earlier to just assume that a is an A, it can not prove that it will have a valB field at runtime.


Now, later on, at execution time, the JVM has more information. When it evaluates a.getClass(), it actually looks up the concrete class that's "under the hood" of a and returns it. Similarly for instanceof B -- it looks up the concrete class and thus the result of that expression is true.

a.toString() works similarly. At runtime, the JVM knows that the thing referenced by a is actually a B, so it executes B's toString method.

Matt McHenry
  • 20,009
  • 8
  • 65
  • 64
  • Can you also address the issue why we can access class B's toString(), yet not able to access class B's variable? – user3437460 Nov 27 '14 at 20:31
0

This is a fundamental property of class inheritance, interfaces, etc. Class "A" does not have a variable "valB". If you want to use the variable "valB" in class "B" either, you should first cast Class "A" to "B"

Try :

System.out.println(((B)a).valB);  
Adam111p
  • 3,469
  • 1
  • 23
  • 18
0

You should know the difference between object type and instance type. First is determined at compile type and at runtime it's doing the best to keep that type safe. Instance type is a class which object is 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.
  • An instance is an object. What do you meant by object type and instance type? For your reference: http://stackoverflow.com/questions/2885385/what-is-the-difference-between-an-instance-and-an-object – user3437460 Nov 27 '14 at 19:06
  • I edited my answer, to be clear. You should better read it. Also I checked that link is useless. –  Nov 27 '14 at 19:12
  • Actually, a is not an object strictly speaking. `a` is just a reference of type `A` (For this I am very certain). My question there is: Since I already pointed variable `a` to object `b`. Why can't it (object b with reference by type `A`) access instance variable of object `b`. – user3437460 Nov 27 '14 at 19:15
  • It has no reference if it's not initialized. Java is strongly typed language, a is of type A and not B. But a can reference an instance of type B, because B is a super class. –  Nov 27 '14 at 19:19
  • Erm.. B is the subclass here. – user3437460 Nov 27 '14 at 19:27
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/65779/discussion-between-user3437460-and-nikpon). – user3437460 Nov 27 '14 at 19:36