2

I am a little confuse on why the below code doesn't work and throw a ClassCastException:

Person superPerson = new Person("marc", 18);        

        PersonOverwritten subPerson = (PersonOverwritten) superPerson;      
        System.out.println(subPerson);

Where the subclass is the same as the super:

public class PersonOverwritten extends Person {

    public PersonOverwritten(String nome, int idade) {
        super();
    }

}

Of course it should fail if they are from different types, but, why this doesn't work?

sylleryum
  • 130
  • 1
  • 9
  • 5
    Because not every dog is a dachshound... – Turing85 Apr 17 '18 at 17:01
  • 2
    A `PersonOverwritten` is a `Person`, but a `Person` is not necessarily a `PersonOverwritten`, and in this case, is not. – rgettman Apr 17 '18 at 17:01
  • Yet another question that is answered with article from the language specification. This type of casting is called [Narrowing reference conversion](https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.6), and there are perfectly clear rules that instruct what cases work and why. – M. Prokhorov Apr 17 '18 at 17:05

3 Answers3

4

Here's a simple example to illustrate. Imagine that PersonOverwritten looked like this:

public class PersonOverwritten extends Person {
    public PersonOverwritten(String nome, int idade) {
        super();
    }
    public void pickupSticks() {}
}

Very clearly you would not be able to do

Person superPerson = new Person("marc", 18);        
PersonOverwritten subPerson = (PersonOverwritten) superPerson;
subPerson.pickupSticks();

because the object assigned to subPerson does not have the pickupSticks method.

The reason that the compiler allows you to write code like that, though, is that you might one day want to do something like this:

Person person = new PersonOverwritten("marc", 18);        
((PersonOverwritten)person).pickupSticks();

In this case, there will of course not be a runtime error, because the object that you have assigned to person does support the extended interface. A very common place to see something like that was before generics were introduced in Java 1.5, and all the collections returned Object when you accessed them.

Update

To elaborate a bit, Java uses types to determine the contract that an object fulfills, rather than a direct inspection of the attributes. It does not use duck-typing, like say Python. The compiler will allow you to cast Person to PersonOverwritten because it might be possible. The runtime will forbid you from using an actual Person object as a PersonOverwritten because, according to Java, it does not fulfill the interface.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
  • Thanks for the response, I get this concept that not necessarily the person is-a personOverwritten, but is this case it is and do have the same methods, so why it still doesn't work? – sylleryum Apr 17 '18 at 17:13
  • 1
    Because Java doesn't compare the contents for deciding the "is-a" relation, but looks at the class inheritance tree. Other languages might behave differently... – Ralf Kleberhoff Apr 17 '18 at 18:10
1

You cannot cast new Person() to PersonOverwritten.

Although the compiler will let this pass, the runtime will detect that the type Person is too broad to fit into PersonOverwritten, which is why you get that ClassCastException.


Just a side note: A similar exception would occur if you had a different, unrelated subclass of the same parent:

public class AnotherPersonOverwritten extends Person {}

The following is also incorrect:

Person subPerson = new AnotherPersonOverwritten();
PersonOverwritten personOverwritten = (PersonOverwritten) new subPerson;
//class cast exception

How do you avoid this: Make sure that the runtime class of the object being cast (the class with which the new keyword is used, in this case) is the same as or the subclass of the class to which the object is being cast.

ernest_k
  • 44,416
  • 5
  • 53
  • 99
0

Assigning an object of parent class to its subclass reference variable will fail. As #Turing85 and #rgettman mentioned in the comment.

Look at the following code:

Dog dog = new Dog();
// Cat cat = new Cat();
// Animal animal = cat;
Animal animal = dog; // here upcasting is automatic
if(animal instanceof Dog) // only if the animal is of type Dog
    Dog dogDup = (Dog) animal; // manual downcasting
/* 
Animal animal = new Animal();
Dog dog = (Dog) animal; // this won't work
*/

See the full article here

To prevent your code from throwing runtime exception, use instanceof operator like this

Person superPerson = new Person("marc", 18);
if(superPerson instanceof PersonOverwritten) // in this case it's "false"
    PersonOverwritten subPerson = (PersonOverwritten) superPerson;

[for more details refer this stackoverflow question]

Hope this will help!

S.Negi
  • 51
  • 7