I wrote a code to understand runtime polymorphism...
class S{
int i=1;
void m(){
System.out.println("sssssssssssssssssssss");
}
}
public class A extends S{
int i=2;
void m(){
System.out.println("aaaaaaaaaaaaaaaaaaaaaaaa");
}
public static void main(String[] args) {
S a=(S)new A();
System.out.println(a.i);
a.m();
}
}
Instance variable are subject to compile time binding, but why down casting of object of A does not meaning here? Means it's invoking A's method not S's method?
S a = (S)new A();
Let's see what you have here:
variable a, of the reference type S;
an instance creation expression yielding an object of type A, where A extends S;
a reference upcast expression, upcasting the above expression into type S;
the assignment of the result of 3. into the variable a.
What you must keep clear in your mind when reading Java is the distinction between:
the type of the object: an object can never change its type. In your example, the object is of type A;
the type of the reference: in your example, you converted a reference initially of type A into a reference of type S. You assigned that reference to a.
When you invoke a method on an object, the actual method invoked does not at all depend on the type of the reference, but only on the type of the object itself. The type of your object is A therefore the method in type A is invoked.
On the other hand, when you access an instance variable, polymorphism does not apply and the type of the reference becomes essential. With a.i you access i declared in the supertype S, and with ((A)a).i you access i from A. Note that the class A posseses two instance variables, both named i, and you can refer to each individually.
A note on terminology
The term "type of a reference" is actually a shorthand for the more correct "type of the expression yielding the reference." It is a purely compile-time artifact: there is no type information associated with a reference at runtime, it's just a bit pattern. Contrast this with the type of the object, which is a purely runtime artifact: the compiler doesn't in general know the type of the object involved in an expression, it only makes assertions about it. When such an assertion fails at runtime, the result is a ClassCastException.
The variable a is a reference of type S to an object whose class is A. When you call m() on that object, you'll always get the version of m() in class A being called, because that's the class of the object, no matter what type of variable is referencing it. That's what polymorphism is about. The version of m() that gets called depends on the class of the object, not the type of the referring expression.
However, this object actually contains two variables called i - one declared in class A and the other in class S. Which one of these you get depends on the type of the referring expression that you use. Since the variable a is of type S, the expression a.i refers to the one that is declared in class S.
Related
The Problem
Consider the code below. B inherits from A, and none of them inherit from String.
I'd like to know the answers to the following questions:
why does the first first cast, B b1 = (B) a1;, produce a runtime error?
why does the first second cast, String b1 = (String) a1;, produce a compilation error?
Why is there a difference? Why doesn't the compiler see the problem in the first case?
The Code
public class Main {
public static void main(String[] args) {
A a1 = new A();
B b1 = (B) a1;
String b1 = (String) a1;
}
}
with class A:
public class A {}
and class B:
public class B extends A {}
A variable of type A could have been assigned an instance of B, because a B is an A. Eg a Dog is an Animal, so a box labelled “Animal” could contain a dog.
But a variable of type A cannot have been assigned a String. Eg A box labelled “Animal” will not contain a Brick.
You may be asking yourself why the compiler doesn’t complain when we can see that the code will clearly fail - there’s no way the variable is a B; it’s an A!
The compiler looks only at the type of the variable when making its checks. It doesn’t examine what code came before. Although your example is simple, checking what a variable actually contains would be an impossible task in the general case.
why does the first first cast, B b1 = (B) a1;, produce a runtime error?
Variables of type A can store instances of B. However, not all instances of A are instances of B.
An A is not a B, but a B is an A. (Like, not all animals are dogs, but dogs are animals).
why does the first second cast, String b1 = (String) a1;, produce a compilation error?
A is not a supertype of String, and String is not a supertype of B.
Why is there a difference? Why doesn't the compiler see the problem in the first case?
Because variables of type A can store instances of B; but variables of type A can never store instances of String.
A variable of type A could in fact be of type B as B extends A. But a variable of type A can never be of type String. That's why the compiler can catch the cast to String, but not the cast to B.
why does the first first cast, B b1 = (B) a1;, produce a runtime error?
Because a1 is an instance of A, but is not compatible with B. Specifically, new A() creates an object that is not compatible with subclasses of A. If the runtime class (i.e., the class with which new was called) of the object is not the same as or a subclass of the target class, casting to that target class will fail at runtime. This is simply because the child class has nothing to do with that object.
why does the first second cast, String b1 = (String) a1;, produce a compilation error?
Even if the actual casting happens at runtime, the compiler performs type checks and prevents pointless operations like this. For this scenario, casting an A object to String is nonsense and the compiler can detect it: there is no relationship between String and A, and the compiler knows what class is a child of what other class. The compiler knows that there is no way in Java for a1 to be an instance of String or of a subclass of String, and that's because String is not a parent of A, the declared type of a1. There are exceptions to this, such as when the cast is begin made to an interface.
Why is there a difference? Why doesn't the compiler see the problem in the first case?
The compiler only validates type casts based on static types (the type of the variable or of the expression). It doesn't look at the runtime class, which of course isn't available until runtime when the object is actually created. When it can determine with certainty that the cast can't possibly be valid (such as in the second case), it will fail. In the first case, casting from A to B passes compilation because the declared types are compatible (i.e., an A object can possibly be an instance of B, and the compiler leaves it for the runtime to check the actual object). In the second case, the compiler knows that an A object can never be an instance of String (because String is nowhere in A's type hierarchy, and this won't change at runtime)
The class hierarchy diagram for the class A would be:
Object -> A -> B (Note that every class extends Object)
B b1 = (B) a1;
The above line compiles because B extends A and hence the compiler sees it as a valid downcast. The java compiler only checks whether it is possible for an object of type A to be of type B, by checking the class hierarchy of B (whether B extends A directly or indirectly). It doesn't check the actual type of the object A at this point. It wasn't implemented this way since it would add a lot of complexity in the compiler. Also if an object is being downcast (to call some specific sub class method perhaps), then the responsibility is on the programmer to be aware of the specific type of the object. In this example since a1 can't be cast to type B, it will be detected by the JVM at runtime.
String b1 = (String) a1;
In this case, the class String is nowhere in the class hierarchy diagram of A. Therefore it can be detected at compile time that this is an invalid cast.
If I have a
class A{ }
class B extends A{ }
and I instantiate a reference variable "var"
A var = new B();
Is "var" then considered to be instantiated as a "B" Object? Where A is the variable reference type and not the Object instantiated for "var".
The object type is still B no matter how you refer to it. That is, the code var.getClass() would return B instead of A, therefore the class of "var" is B.
You could also say that "var" is an instance of A and the code boolean isA = (var instanceof A) would return true, but var.getClass() would not return A.
var is a reference of type A. It points to an object of type B. However, it only knows about the methods that are exposed in A. So, if class B defines a method not present in A, you won't be able to call them from the var reference.
Yes it is going to be successfully casted to A, but you are not going to be able to access any B specific properties from A . See Polymorphism
A and B are both classes. A class is a blueprint, and an object is an instantiation of a class.
However, a variable neither is an object nor holds an object. A variable only holds the location of an object. Therefore, object variables (i.e. non-primitive variables) are called reference variables in Java.
In this specific case, we would say that the variable var is of type A but references an object of type B. Even though var references a B object, it can only access the methods defined in parent A because it is of type A.
A var = new B();
This is a typical example of upcasting in Java.
RHS indicates that object of type B is instantiated in the memory by its default no-args constructor. But you need a reference variable to point to the memory. So in LHS "A var" , var is the reference variable of type A . Since in Java, runtime polymorphism or Dynamic method dispatch is allowed in which call to the overridden method is resolved at runtime rather than compile time and the overridden method is called through the reference variable of the superclass.
So in short. Object of B is instantiated and for achieving runtime polymorphism, it is reference variable of type A.
Hope this understanding will help in discovering your answer.
I've been trying to understand casting in Java and how it affects the references. I've come up on this particular example:
public interface A1{
public void foo();
};
public class A2{
public void bar();
public static void main( String[] args )
{
A2 a = new B();
A1 c = (A1)a;
c.foo();
}
};
public class B extends A2 implements A1{
public void foo(){ System.out.println("This is B"); }
}
It prints "This is B", but i'm not sure why. This is how I understand this currently: a is a reference to an object of type A2, but during runtime, it points to a heap object which has the properties of B, but a only "sees" the properties of A2. But the types are already determined during compilation, so the cast tries to cast A2 to A1, which it can't do. Obviously I'm wrong, but I can't figure out why. Any help will be appreciated.
Casting conceptually has two components,
At runtime the JVM ensures that the object is of that type, and
will error with a ClassCastException if it is not
At compile time, it tells the compiler to allow use of the methods/fields of
that class. Note that if the object turns out to not be of that type at runtime, then it will error at runtime.
What casting does not do, is change the type of the actual object at runtime.
In your example you called new B(); that means after casting a reference from B to A1, then the object is still an instance of B. Given that foo() is declared on A1, and B extends foo then the compiler is happy (it passes 2 above). At runtime the JVM will scan B looking for the method of foo, it checks B before A1 because the object was created as type B. It did not change its type since new was called.
Casting checks are always made at runtime (see caveat in next paragraph), your reference points to an object of type B, therefore when you get to the casting bit, the VM will see a reference to an object of B, which can be safely cast. Note that casting doesn't actually change the object, it just ensures that the rest of the code calls methods which are available in the original object.
The caveat is that if it can be seen at compile time that the cast is definitely NOT possible, you do get a compile error.
String foo = "x";
Integer i = (Integer)foo; //throws a compile time error because `String` is final and `Integer` isn't its supertype.
But:
Object foo = "x";
Integer i = (Integer)foo; //this will only throw a runtime exception because from the compiler's point of view, `foo` may or may not be an integer.
The type of the variable that holds the reference has nothing to do with what the method implementations resolve to. In Java, all methods are implicitly virtual, meaning that they get looked up by the actual type of the object (instead of the type of the referring variable) every time. Since c actually points to an instance of B, it's B.foo() that gets called.
Note that this doesn't necessarily hold true for all languages - in C#, for example, methods are only virtual if you explicitly declare them as such and so i its default behavior would match what you were thinking. Java, however, is always virtual.
Reference comes into picture only during compile time or in case of static methods or behaviors.
When you are creating an object, method or behavior will depend on the whose object you have created rather than whose reference you have used.This is called polymorphism.
For example lets take an example of real life entities ---
When you rent a house, you ask a broker to find a house for you, which is your reference.but the person whom house belong, the land-lord is the actual object whom you are requesting. so it is the land-lord who gives you house on rent and not the broker.
Say I have the following java code:
Object myObj = new ArrayList();
List myList = new ArrayList();
I would like to know how to determine which class or interface a given object is typed with.
Note that I am not talking of the type the object was instanciated from (here ArrayList) but rather the type on the left-hand side of the expression so that would be in the example above Object for myObj and List for myList.
How do I call this "left-hand side" type in proper parlance?
Also, how do I call the "right-hand side" type?
How do I determine programmatically what is this "left-hand side" type?
Regards,
The type used on the left-hand side is not the type of an object. It's the type of a variable. (The static or compile-time type.)
It's worth differentiating between a variable and an object - a variable has a name, a type and a storage location. The storage location contains value compatible with the type of the variable.
Now to find the type of a variable at execution time, you'd need some sort of reference to the variable itself as a variable. For example, if it's a field (static or instance) you can fetch the appropriate Field reference, you can ask for the type using Field.getType().
If it's a local variable you'll find it a bit harder. If you can explain what you're trying to achieve (rather than the mechanism you think will help you achieve it) we may be able to help more.
Calling myList.getClass() or anything like that will not do what you want, because that will deal with the value of myList, not the variable itself.
The "left-hand side" type is called the static type.
The "right-hand side" type is called the dynamic type.
You can determine the dynamic type by calling var.getClass().
One way to determine the static type is by using function overloads:
public static void f(Object var) { ... }
public static void f(List var) { ... }
When you call f(var), the compiler will use the static type of var to call the correct function.
In Java, an Object can have a runtime type (which is what it was created as) and a casted type (the type you have casted it to be).
I'm wondering what are the proper name for these types. For instance
class A {
}
class B extends A {
}
A a = new B();
a was created as a B however it was declared as an A. What is the proper way of referring to the type of a using each perspective?
I think it's important to distinguish between the object (which exists at execution time, and just has its execution time type) and an expression (such as a variable) which has a compile-time type.
So in this case:
A a = new B();
a is a variable, of type A. Its value at execution time is a reference to an object of type B.
The Java language specification uses "run-time class" (e.g. for the purpose of overriding, as in section 15.12.4.4) for the type of an object. Elsewhere I think it just uses "type" for the type of an expression, meaning the compile-time type.
The Java Language Specification speaks about a variable's declared type, the javadoc of getClass() about an object's runtime class.
Note that there is no such thing as a runtime type in Java; List<String> and List<Integer> are different types, but their instances share the same runtime class.
In this case, A is the reference type while B is the instance type
I would say that you differentiate between the type of the variable/reference and the type of the object. In the case
A a = new B();
the variable/reference would be of type A but the object of type B.
The type of the variable a is A. There's no changing that, since it's a reference. It happens to refer to an object of type B. While you're referring to that B object through an A reference you can only treat it as though it were of type A.
You can later cast it to its more specific type
B b = (B)a;
and use the B methods on that object.
The terminology you are looking for is the Apparent Type and the Actual Type.
A a = new B();
The Apparent Type is A because the compiler only knows that the object is of type A. As such at this time you cannot reference any of the B specific methods.
The Actual Type is B. You are allowed to cast the object (that is change its apparent type) in order to access the B specific methods.
To determine a is object of which class you can use:
/*The java.lang.Object.getClass() method returns the runtime class of an object*/
System.out.println("a is object of: "+a.getClass());
Section 15.5. Expressions and Run-Time Checks differentiates between
the type of an expression
the class of the referenced object
For example,
If the type of an expression is a reference type, then the class of
the referenced object, or even whether the value is a reference to an
object rather than null, is not necessarily known at compile time.
There are a few places in the Java programming language where the
actual class of a referenced object affects program execution in a
manner that cannot be deduced from the type of the expression...
[snip]
An expression whose type is a reference type may be tested using
instanceof to find out whether the class of the object referenced by
the run-time value of the expression
Hence, applying the above language to
A a = new B();
we might say something like
The static type of the expression a is A, despite the fact the value stored in a is a reference to the object of runtime class B.
Personally, I interpret the two concepts in the following manner (but beware I am unsure of its correctness):
static type of the expression is purely syntactic concept existing in source code at compile time for processing by Java compiler
runtime class of the object is actual machine construct existing in machine memory at runtime for processing by Java Virtual Machine