I thought you can't cast Parent to Child, and can only cast Child to Parent. Or at least if you did, you wouldn't be allowed to access the child's functions.
Then I see that Java will allow me to explicitly cast parent to child:
a=(B)a;
but will not allow
bArray[0]=a
or
a.testB();
Yet this is ok:
bArray[1]=(B)a;
and so is this:
bArray[1].testB();
Can someone please explain what's going on here!? How can a parent class be given functions of a child class? And why does the first attempt at placing a into the array not work, but the second attempt does? Shouldn't it be the same?
class A {
public void testA() {
}
}
class B extends A {
public void testB() {
}
}
public class polymorphicObjects {
public static void main(String[] args) {
B bArray[] = new B[5];
A a = new A();
A ab = new B();
a = (B)a; //didnt think you could do this
bArray[0] = a; //but if I can the why doesnt this work?
a.testB(); //or this
bArray[1] =(B)a; //but this does work
bArray[1].testB(); //and so does this!?
}
}
Does running bArray[1].testB(); actually work? That should throw a ClassCastException at least. Adding a print statement inside the testB() method should help with that.
So in this, you're saying a = (B)a; which is very explicitly casting a to type B, but it doesn't actually mean anything.
With that in mind, this doesn't work in the first line, bArray[0] = a; because it's no longer explicitly cast, so the compiler will complain that you're giving a type that doesn't match.
In the subsequent line bArray[1] = (B)a; you are explicitly telling the compiler that a is of type B, so it won't bark at you... although the call to the method testB() should fail at runtime.
In Java, casting has a very specific meaning (different from the C language, e.g.). You have to distinguish what class your object actually has, and what the compiler thinks about the class of some expression.
Casting doesn't change the actual object class, but only what the compiler thinks about the class of the expression.
Let's walk through your code:
new A() will always give you an object of class A, and the compiler knows that.
You store that into a reference variable a, declared to be of class A, so from the declaration (not the new A() expression) the compiler knows that variable a has class A (allowing subclasses as well).
With the cast expression (B) a you don't change anything about the object that's stored in a, just assert to the compiler that you, as the developer, know that the object stored in a is of type B (which is not true, in your case, it's an A, not a B). The compiler isn't intelligent enough to see that, so he treats the expression (B) a as valid and of class B. He allows you to cast from A to B because B is a subclass of A, so a variable of class A might perfectly well contain an object of class B (e.g. A a = new B(); would be perfectly OK). At runtime, the JVM will check for the actual object class and raise a ClassCastException as it finds out that it's an A not coming from subclass B.
With a = (B) a; you store your same object back into the same variable where it came from.
You ask about bArray[0] = a; This doesn't compile because for the compiler, a contains an object of class A, and the array needs a B. bArray[1] = (B) a; will compile, but throw a ClassCastException when run as long as the variable still contains the same object of class A.
And bArray[1].testB(); compiles because the compiler knows that in the bArray all elements are of class B, and class B has this method. As trying to store an object which isn't of class B into bArray won't work (either at compile-time bArray[0] = a; or at run-time bArray[1] = (B) a;), calling the testB() method is safe.
Related
This question already has answers here:
A Base Class pointer can point to a derived class object. Why is the vice-versa not true?
(13 answers)
Closed 7 years ago.
This is rather basic question. But I can't understand well the concept of inheritance.
Suppose I have two classes, A and B with both have a test() method that returned 1 and 2 respectively, and B inherited A class. In main method I declare the instance as such;
A a1 = new B();
and call the method a1.test(), it will return 2. This is the concept of polymorphism. But when I have a method test2() in just subclass, I can't call the method using the same instance declaration as above. Why is that happen?
I can't call the method using the same instance declaration as above. Why is that happen?
Because the type of the variable is A, and class A does not have a method test2(). The Java compiler only looks at the type of the variable to check if you can call a method, it does not look at the actual object (which is in this case a B).
This is all easier to understand if you use more concrete and meaningful names for your classes, instead of abstract names such as A and B. Let's call them Animal and Bear instead:
class Animal {
}
class Bear extends Animal {
public void growl() { ... }
}
class Cat extends Animal {
public void meow() { ... }
}
Animal a1 = new Bear();
Animal a2 = new Cat();
// Doesn't work, because not every Animal is a Bear, and not all
// animals can growl.
a1.growl();
// You wouldn't expect this to work, because a2 is a Cat.
a2.growl();
Because variable type is A, and class A does not have a method test2():
Rather you can use:
A a1 = new B(); // upcasting
B b1 = (B)a1; // Downcasting a1 to type B
b1.test2(); // now you can call test2 function
Because, the left side of your condition determines which method's you can call, and right side determines which methods will be called. So in this case class A does't have test2() method.
Imagine A = "TV" and B = "HD_TV".
You can say
TV tv = new HD_TV() // HD TV
and
TV tv = new TV() // ordinary TV
because an HD_TV is a TV.
You can say:
tv.show(movie)
It will show what is on TV, but you will get a better picture with the HDTV.
You cannot say:
tv.showHD(hdMovie) // Compiler error !!!
because in declaring tv as TV, you are saying it might not be an HD TV. Even though you can see that in this case it is, the compiler still respects your declaration that it is just a TV and you can only use methods supported for a TV.
That is because you are declaring the instance a1 as an A. Because B inherits A, you can call all the functions declared in A and they might have a different meaning if they are overloaded in B, but you do not have any access to B-only things.
You can see the first A as some kind of a header file, if you are familiar with that. It declares what A contains, without looking at how the functions are implemented or what the default vars are of everything in A. As a direct consequence, you can only access everything that is declared to literally be in A.
The left-hand side - A in this case - is the declared type, and it doesn't know about anything specific to child classes. The right-hand side - ´B´ in this case - is the actual type, and this provides the behaviour.
So, this will work because the declared type B knows about methods available in the class B.
B b1 = new B();
b1.test2();
If it was possible to have a1.test2(), that would mean every class would have to know about every child it has - including those in other libraries, when projects are assembled!
When B inherits A class and the reference of A is created with object of B like A a1 = new B();.
On Compile time java compiler looks for method availability in class A.
So it allows calling method test() but not the test2().
As test() method is available in class A but test2() is not available in class A.
You can type cast the object created like ((B)a1).test2().
This will work.
Here a1 object is of type A. a1 is pointing to an object of type B.
a1 is a reference of type A to an object of type B.
since a1 is of type A it know only test() which is declared in its class definition already. In case you want to access test2 declared in class B you need to type cast the a1 object back to B
like
B b1 = (B)a1
b1.test2() will be accessible.
This happens because you declare A variable and use B class which is an A. The compiler know it's an A but doesn't know it's a B later in the code. It's easier to use real life objects as example.
For example you have:
class Pet() {
method feed();
}
And a
class Dog() extends Pet {
method feed();
method bark()
}
If you have a code in another class:
So if you have code :
Pet dogPet=new Dog();
You know it's a dog here because you create the instance and you can use:
((Dog)dogPet).bark(); or just declare the variable as a dog instead of pet.
But if you have a method in another class:
void someMethod(Pet aPet){
// Here you don't know if the Pet is a dog or not. So you know only that it
//can be fed but you don't know if it barks. Even if a Dog is supplied to the method
}
In a1 = new B(), the actual type of the object created is B but you reference it as its supertype so you can call a method that accepts A (polymorphism).
So if a method is overridden in subclass, a1.test() is executing subclass's test().
In order to execute test2() you have to do that: ((B) a1).test2();
There is a concept called Up casting and Down casting.Up-casting is casting to a supertype, while downcasting is casting to a subtype. Supercasting is always allowed, but subcasting involves a type check and can throw a ClassCastException.,See the Example Code:
class A{
public int test(){
return 1;
}
}
class B extends A{
public int test(){
return 2;
}
public int test2(){
return 3;
}
}
and
A a1 = new B();
a1.test2();//not possible
Here you can't invoke methods of class B.
This question already has answers here:
Cast reference of known type to an interface outside of type's hierarchy
(6 answers)
Closed 8 years ago.
interface I{}
class A implements I{}
class B{}
First:
I[] arr = new A[10];
arr[0] = (I) new B(); // will produce ClassCastException at runtime
Second:
wherein if I use concrete class
I[] arr = new A[10];
arr[0] = (A) new B(); // will produce compile-time error
What's the difference if in my first example,(I) new B(), the java compiler should produce compile-error as well?
Isn't it that the java compiler should be able to distinguish that it is also an "inconvertible type"? Especially when the new operator comes immediately?
Is there any instance/chance that it will be possible that creating that new instance of B could produce a
convertible type of type I?
I know at some point, that the Java compiler should not immediately say that it is a compiler error, like when you do this:
I i = (I) getContent(); // wherein getContent() will return a type of Object
Edit:
Let me clarify this question why it is not a possible duplicate of this: Cast reference of known type to an interface outside of type's hierarchy
The intention of this question is not because I am not aware of what will be the result of or what is something wrong with something, etc.
I just want to know a "more detailed explanation in a technical way" of why does the JVM behave that way or why does Java came up with that kind of decision of not making that kind of scenario a compile-time error.
As what we all know, it is always better to find "problematic code" at compile-time rather than at run-time.
Another thing, the answer I am looking for was found here on this thread not on those "duplicates?".
The rules for what casts are compile-time legal only take into account the static types.
When the Java compiler analyzes the expression (I) new B(), it sees that the static type of the expression new B() is B. We can tell that new B() can't possibly be an instance of I, but the compile-time analysis rules can't tell that the object isn't actually an instance of a subclass of B that implements I.
Thus, the compiler has to let it through. Depending on how sophisticated the compiler is, it might detect the oddity and issue some sort of warning, but in the same way 1/0 isn't a compile-time error, this can't be a compile-time error.
The difference in this situation is obviously that I is an interface, which could be implemented. This means, that even though B has nothing to do with I, there could be a subclass of B, that implements the interface I. Will illustrate this with an example:
interface I{}
class A implements I{}
class B{}
class C extends B implements I{}
I[] arr = new A[10]; // valid, cause A implements I
B b = new C(); // Valid because C is a subclass of B
arr[0] = (I) b; // This won't produce ClassCastException at runtime, because b
// contains an object at runtime, which implements I
arr[0] = (I) new B(); // This will compile but will result in a ClassCastException
// at runtime, cause B does not implement I
It's important to distinguish the difference between static and dynamic types. In this case the static type of the variable b is B, but it has the dynamic type (the runtime type) C, where new B() does also have the static type B, but the dynamic type is also B. And as in some cases a cast from B to I won't result in exceptions (as in this scenario), the compiler allows such casts but only to an interface type.
Now take a look at the following scenario:
I[] arr = new A[10];
B b = new C(); // Valid because C is a subclass of B
A a1 = (A) b; // compile time error
A a2 = (A) new B(); // compile time error
Is it possible that a subclass of B will ever extend A and B at the same time? The answer is NO, cause you are limited to only one super class to extend in Java (where in some other OO languages this is not the case), therefore the compiler forbids it, cause there are no possible scenarios, where this will work.
You can cast any object to the desired var, only if you cast it to the var type:
interface I{}
class A implements I{}
class B{}
I var = (I) object; // This is always possible in compile-time, no matter the object type, because object is casted to the var type, I
I var = (A) object; // Not possible in compile-time because of the difference of types
The runtime exception comes when the object cannot be casted, but you can't know it until runtime.
A object = new A();
I var = (I) object;
B anotherObject = new B();
var = (I) anotherObject;
Both of the above will work in compile-time, but only the first one will do it in runtime, because of the implementation of the I interface.
Class inheritance is single inheritance, no future descendant can introduce a new base class, e.g. in your example, no descendent of B can ever be cast to A. But it can introduce a new interface, i.e. a descendent of B could support I.
It's a failing of the compiler that it can't solve your simple case, but it's not a case you would ever see in the wild. Create and cast in one line that is.
Example of why the compiler can't detect this in a more complex case using your classes
void method(B b){
I i = (I) b;
}
class C extends B implements I{} // a descendent of B that introduces support for I
method(new A()); //still compile time error
method(new B()); //runtime exception
method(new C()); //works
I tried four cases:
Casting a class to another class.
Casting a class to an interface.
Casting an interface to a class.
Casting an interface to another interface.
Compile-time error only happens in the first case.
static interface I {}
static interface J {}
static class A {}
static class B {}
Object o = (B) new A(); // compile-time error
Object o = (I) new A(); // runtime error
Object o = (B) ((I) new A()); // runtime error
Object o = (J) ((I) new A()); // runtime error
My guess is that this happens because determining whether the cast will succeed or not relatively easier in the first case, compared to the other three cases. The main reason is that a class can only extend one class, which allows the compiler to reason whether the cast will succeed or not. See these examples:
Suppose, in addition to the above classes and interfaces, I add new class:
static class C extends B implements I, J {}
Example of code that casts a class to an interface (last line):
C c = new C();
B b = (B) c;
I i = (I) b; // This is ok.
Example of code that casts an interface to a class (last line):
C c = new C();
I i = (I) c;
B b = (B) i; // This is ok.
Example of code that casts an interface to another interface (last line):
C c = new C();
I i = (I) c;
J j = (J) i; // This is ok.
Is there a work around that will allow me to cast an object of the base class to an object of the derived class?
something like the following
B extends A
A a = new A();
B b = (B)a
Is there a trick that will achieve this?
No, absolutely not. What would you expect the values of any fields declared in B but not in A to be? For example, what would you expect this to do:
Object x = new Object();
String text = (String) x;
System.out.println(text);
An Object has no text data... so what would it mean to cast it as a string?
You can only cast a reference to a type which is appropriate for the actual type of the object.
The desire to do this usually indicates a design problem somewhere... or it might mean that you want something like:
public class A {
public A() {
// Whatever
}
public A(A a) {
// Use the existing values in "a" to initialize this object
}
}
public class B extends A {
/** Creates a new B from the values in an A, with suitable defaults. */
public B(A a) {
super(a);
// Now initialize any fields in B with appropriate values
}
}
Then:
A a = new A();
B b = new B(a);
That will create two objects, unlike a cast... but it would at least be valid.
How is that even possible? Think about it. It is like saying if you have a class FourWheeler, you can simply cast it into a Ferrari and make it a Ferrari!
No, this isn't possible. When B extends A it inherits the behavior of A, but on the same time, there is nothing stopping you from defining new behavior for B (where those new behaviors won't be part of A)
For example say A has a single method called 'methodA'. Now when B extends A it inherits 'methodA' but it also declares another method called 'methodB'. So under such circumstance you will get a runtime 'ClassCastException' when you try to call the 'methodB' over an instance of Object A.
Ok.. So,
When you have a hierarchy of classes such as
public class A {...}
and,
public class B extends A {...}
...When you create objects, what is the difference between:
A object = new A();
A object = new B();
B object = new B();
Thank you for your time.
public class A
{
public void methodA(){}
}
public class B extends A
{
public void methodB(){}
}
I hope this can demonstrate the difference.
A obj = new A();
a.methodA(); //works
A obj = new B();
obj.methodA(); //works
obj.methodB(); //doesn't work
((B)obj).methodB(); //works
B obj = new B();
obj.methodA(); //works
obj.methodB(); //works
A object = new A();
You are creating an A instance in a reference of type A. You may can access only A methods/properties and parents methods/properties.
A object = new B();
You are creating B instance in a reference of type A. In this way object could behave in a polymorphic way, for example if you make object.method() and method is overriden in B then it will call this override method. You have to take care in not to break the Liskov Substitution Principle. You may can access only A methods/properties and parents methods/properties. This is the preferred way when you only need supertype contract.
B object = new B();
You are creating a B instance in a reference variable of type B. You may can access only B methods/properties and parents methods/properties.
A line like
A var = new B();
is kind of a shorthand for two separate steps.
A var; // (1) Make a variable of TYPE A.
var = new B(); // (2) Make an object of CLASS B, that from now on may be
// referred to by the variable var.
So a variable has a TYPE, and an object has a CLASS. Often they match up. The type of a variable is often actually a class, although not necessarily. It's important to understand the difference between the type of a variable, and the class of the object that the variable refers to.
An object typically belongs to more than one class. If class B extends class A, that means that all objects of class B are also objects of class A. And all objects of any class at all are also objects of class Object. In other words, when we say that an object is a B, that's more specific than saying it's an A. Just like when we say that Yogi is a bear, that's more specific than saying Yogi is an animal, because all bears are animals.
So a variable of type A can indeed refer to an object of class B, if A is a class that B extends. But if you've got a variable of type A, you can't use it to do things that are specific to objects of type B. For example, suppose class A has a method called display() and class B has a method called explain(). The compiler will let you call display() on a variable of type A, but it won't let you call explain(). If it did, it would be risking trying to call explain() on an object that's not actually a B, which would fail.
So whenever there are methods that class B defines, you'll need a variable of type B in order to be able to call them. Of course, you can also use that same variable to call the methods that are defined in class A. In a sense then, if class B extends class A, then a variable of type B is more powerful than a variable of type A - you can do more stuff with it.
So the question arises - why would I ever want to write
A var = new B();
when a variable of type B would be more powerful than var in this example?
The short answer is that it communicates to people looking at the code. It says, "yes, I know this variable refers to a B, but I actually only intend to use the methods provided by class A. This can actually be helpful to someone trying to understand your code, or to maintain it.
There are also cases where it can make a real difference to method calls involving that variable. Suppose there's another class C, which has two methods with the same name but slightly different signatures, like this.
public class C {
public void process(A arg){
// Do some stuff
}
public void process(B arg){
// Do some other stuff
}
}
In this particular case, the version of process that gets called depends on the type of the variable, not the class of the object. So if you write
C processor = new C();
A var = new B();
processor.process(var);
this will call the first version of process - the one with A in the signature. Because of the type of the variable. But if you write
C processor = new C();
B var = new B();
processor.process(var);
this will call the second version of process - the one with B in the signature.
A object = new A();
object of type A (you can access fields or method from A)
A object = new B();
object of type A (you cannot access fields or method from B, only from A)
B object = new B();
object of type B (you can access fields or method from A and B)
A object1 = new A();
A object2 = new B();
B object3 = new B();
object1 is declared as a reference to an A object. Since class B extends class A, it could be set to either or (new A() or new B() would be valid).
object2 is declared as a reference to an A object, but is actually a B object. Say the B class has a method called eatFood(). If you tried to access that method with object2.eatFood(), the compiler would throw an error because the eatFood method is only in the B class. Even though the object is actually a B object, the compiler thinks it is an A object due to the type declaration. To access the eatFood method, you would have to typecast it: ((B)object2).eatFood().
object3 is simply a reference to a B object, and in reality IS a B object. It could access A methods as well as B methods.
A object = new B();
This declares that object will refer to an object of class A or any of its subclasses (when it isn't null). The compiler will treat it as an object of type A, so you can only access methods and fields that are declared for A (or one of its superclasses). It also means that you can later assign it to any other object that is of class A or a subclass:
A object1 = new B();
B object2 = new B();
// reassign later
object1 = new A(); // legal
object2 = new A(); // ILLEGAL
class C extends A { ... }
object1 = new C(); // legal
object2 = new C(); // ILLEGAL
So the initial declaration declares object as having type A. But its initial value is an object of type B, which is OK because B is a subclass of A.
That should explain the difference between your second and third examples. The difference between the first and second is simply that (at run time) the first creates a new object of type A and the second creates a new object of type B.
Okay. So if...
int x=3;
int y=5;
x=y;
That'll make x=5, right?
Okay, so if B is a subclass of A...
A a=new A();
B b=new B();
a=b;
^^^Why is this considered upcasting?
Isn't the "a" supposed to become the "b" and not the other way around? Can someone explain all this to me?
Instead of As and Bs, let's jump to a concrete example.
class Person {
public void greet() {
System.out.println("Hello there!");
}
}
class ComputerScientist extends Person { // Does he, really?
#Override
public void greet() {
System.out.println("Hello there! I work at the Informatics Department.");
}
public void validateAlgorithm(Algorithm a)
throws InvalidAlgorithmException {
// ...
}
}
When you have a ComputerScientist as
ComputerScientist cs = new ComputerScientist();
You can access both greet and validateAlgorithm. You know (s)he is a Person, and can greet him/her as any other person. However, you may also treat him/her specifically as a ComputerScientist.
When you assign this object to a variable of type Person, all you do is saying "I don't care anymore that you are a ComputerScientist. From now on, I will treat you just as any other Person".
Person p = cs;
Which is equivalent to
Person p = (Person) cs;
The object referred by p still knows how to validateAlgorithm, and still tells you that (s)he works at the Informatics Department. However, when accessing it via p, you are telling the compiler that you only want to greet this Person, nothing else.
It is called upcasting because the variable goes up in the hierarchy tree, where up means more general/abstract and down means more specific. You're generalizing a ComputerScientist as a Person.
After a = b;, the variable a (declared with type A) will refer to an object of type B. Thus the assignment involves an implicit upcast: a = (A)b;, converting how Java views b from B to its superclass A. That's an upcast.
A a = new A();
B b = new B();
The flow is as follows:
Object of A is created using new A() and ASSIGNED to refrence variable a
similarly Object of B is created using new B() and ASSIGNED to the refrence variable b
The point to note here is that the evaluation goes from the right side to the left side which is why first the Right side is calculated and the results are assigned to respective variables.
Now coming to your problem which is why a=b is UPCASTING
The above mentioned points apply to this statement as well, first b is evaluated which is a subclass of a. Now since you are assigning a subclass to a superclass an implicit casting takes place from a subtype to a super type which is ofcourse UPCASTING.
This link will make it more clear https://www.youtube.com/watch?v=Wh-WZXCAarY
Hope this helps.
That'll make x=5, right?
Right.
Okay, so if B is a subclass of A...
^^^Why is this considered upcasting?
Because that's what it is.
Isn't the "a" supposed to become the "b" and not the other way around?
Yes, and it does. The reference to B is upcast to a reference to an 'A'.