Search code examples
javainheritanceextends

java do extended classes have two types


In java I have the following:

ClassA obj = new ClassB();

where ClassB extends ClassA. Is obj of type ClassA or ClassB or both?

if we have instead:

ClassB obj = new ClassB();

it seems clear that obj is of type ClassB but all ClassB are ClassA too, aren't they? So is the type both ClassA and ClassB?

if we add the following:

ClassA [] objects = new ClassA[3];
ClassA obj = new ClassB();
objects[0] = obj;

it seems that obj is of type ClassA. So we can add it to objects even though obj is a new ClassB();

Here's my confusion, let say we have an overwritten method in ClassB from ClassA, I can call the method and I get the expected output from the ClassB overwritten method. But, if I have just a ClassB method not also contained in ClassA I can't call it and I don't seem to be able to upcast it to ClassB. oh wait, obj is of type ClassA so we can't cast it to a ClassB because all ClassA's aren't ClassB's.

So obj is of type ClassA but we set it to new ClassB(). so what the heck is it? Is it ClassA or ClassB, I'm so confused. Why can we access the overwritten methods in ClassB but not the non overwritten methods? Is that just the way polymorphism works?


Solution

  • Your question is identical to this situation:

    You see Max, who is a poodle. You ask: So, what's the deal? Is Max a Poodle, a Dog, or an Animal? Or all three at the same time?

    The answer is more or less 'that question belies a misunderstanding of how we classify living things'. If you must choose from your limited set of options, "It is all 3" is more correct than any other. But that implies you could create a Poodle that is also an Animal but not a Dog, and that is the wrong conclusion: Anything that is a poodle is necessarily also a Dog.

    It also depends on context. If I ask about the 'type' of creature that Max is, and the situation calls for just one answer, you always get 'Poodle'. Because that's the most useful answer - knowing that max is a Poodle is enough to determine that max is therefore necessarily also a dog and also an animal because by definition, all poodles are. You can't have a non-dog creature that is nevertheless a poodle.

    Hence:

    • Animal a = new Poodle(); is perfectly legal. Al poodles are animals, so, if you have a job that requires any animal, a poodle will work. So would a chihuahua.

    • Java does everything by reference. Animal a; is like declaring the existence of a little post-it note named a which is to contain the adress of where some animal lives. The address can even be left blank (null). You are guaranteed that, if an actual address is on this post-it note, that it will definitely lead to some sort of animal (it would not lead to, say, an oak tree, the language and runtime guarantee this). In this case, it would, in fact, lead to a poodle. Which works fine: all poodles are animals, after all. The fact that your address postit says 'I might lead to any animal' doesn't magically turn max into a donkey or even into a 'generic animal' - max remains a poodle:

    Poodle max = new Poodle();
    Animal a = max;
    System.out.println(a.getClass()); // prints 'Poodle'.
    

    Remember, the dot operator is 'follow the address on the post-it and ask the thing you find there a question'. .getClass() doesn't 'ask the question' to a, it asks the question to the thing a is pointing at. Currently, a is pointing at max. And if you ask Max what he is (and if dogs could speak, I guess), max would say 'I am a poodle'. Max won't say "I am an animal"; max has no awareness of the postit note, and really couldn't: There could be 85 postits all with the address of max. There could be none left (local variables cease existing once a method ends, for example, as if the postit note poofs into oblivion). When asking questions to objects they don't get the 'context' (what route you used to find that object), and thus you know their answer cannot possibly be dependent on it.

    • Poodle b = a; is not legal. This means: "Take the address on post-it note a and copy it over to post-it note b, which is restricted such that, if it points to anything, it points to a poodle. Even if a is currently pointing at a poodle, at compile time the compiler doesn't know that, so it won't let you do this. Not without a cast: Poodle b = (Poodle) a; is fine, because that is interjecting: "Check if a is in fact pointing at a poodle. If it is indeed, then all is well. But if not, stop everything you are doing and exit this procedure abnormally with a 'ClassCastException'.

    • Objects always have many, many types. For example, new ArrayList<String> is all of these things: Serializable, RandomAccess, Iterable<String>, ArrayList<String>, AbstractList<String>, List<String>, AbstractCollection<String>, Collection<String>, Object, and perhaps a few more I missed. It's not just the type hierarchy (ArrayList extends AbstractList extends AbstractCollection extends Object), it's also all the interfaces you pick up along the way - and all of their superinterfaces too. This matches real life. max is a poodle, a dog, an animal, a meat eater, a legged thing, a source of pee, a 2-eared thing, male, and so many other things. All at the same time.