简体   繁体   中英

Polymorphism in Java and creating objects from these classes

I'm a little confused exactly about how polymorphism and extending classes etc... works in Java when creating objects from these classes.

I recently had a problem which someone on here helped me resolved (see Not able to access object sub class's variable? (Java / Android) ) for background.

I was attempting to create an object like so:

Quad hero = new Hero();

Where Hero was a subclass of Quad();

I had variables in my Hero class which I wasn't above to access.

The solution was to change my object creation to:

Hero hero = new Hero();

doing this, through my hero object, I was able to access all methods and variables in both my Quad and Hero classes.

My question now is - Why is this?

And with this in mind, when would it be useful to use my original method:

Quad hero = new Hero();

That one makes sense to me as Hero is also a quad. I've seen this type of declaration many times in Java code examples, and thought I understood, it but recent events have proved otherwise.

Would be grateful if someone could explain this for me - thanks

Assume OtherHero is also a subclass from Quad with different behaviour:

This will work fine:

Quad hero = new Hero();
// some code
hero = new OtherHero();

Whereas the following won't compile:

Hero hero = new Hero();
// some code
hero = new OtherHero();

This doesn't seem to be useful. Now assume you have a method, that has Quad as return type. I'll write it in pseudocode:

Quad method() {
    if (condition)
        return new Hero();
    else
        return new OtherHero();
}

So you actually don't know if it will return a Hero or an OtherHero

Now, you can write:

Quad foo = method();

Now, you actually don't know the exact type of foo , but you can use it as Quad .

A popular example is java.util.List

You can write:

List<Integer> list = Arrays.asList(1,2,3,4,5,6);
Collections.shuffle(list);

List is an interface, implemented by ArrayList , LinkedList and many other classes. Without knowing the code, we don't know which class exactly is returned by Arrays.asList() , but we can use it as a List .

Declaring the variable type as Quad , means that the variable also can reference other types of subclasses of Quad , not only Hero . And if you pass that reference around, the receiver would only know that it is "some kind of quad", and can't rely on it being a Hero . Hence, it can not access the fields of the reference that are declared only in the subclass.

As for when to use that kind of assignment, I usually only do it when working with interface types. Like:

public List<SomeObject> doSomething() {
    List<SomeObject> result = new ArrayList<>();
    // do something
    return result;
}

But it's really just a habit.

If you store your object in a variable of Quad then, for all the compiler knows, the object stored in that variable is a Quad and nothing more. It may be of a subtype, sure, but the compiler has no idea what that subtype is. For all it knows, that subtype will be introducing absolutely zero members to the base type. If the compiler has no guarantee that the object is anything more than a Quad , then it doesn't want to allow you to do anything more than what it would allow you to do with a Quad .

Granted, you can see that this variable is only ever assigned a new Hero() expression. And, granted, you may know that a specific variable will only hold objects of the Hero type. But this is runtime information that you can deduce in practice. The compiler can't bother to calculate all these scenarios. That's why it has you. If you know that objects stored in that variable will always be of type Hero , you can go on and express it to the compiler.

One reason the language allows you to cast an object to a super type is because in many occasions you want to pass that object to a method that doesn't have to know about all the possible subtypes of the base class (say, Hero ) but only that the passed object is at least of a base class (say, Quad ). Another typical scenario is when you want to store various objects that are not all of the same specific type (they are not all Hero ), but they all have a common ancestor (they are all Quad ) and you want to do something with them, collectively. In general, then, casting objects to an ancestor class is permitted because it is useful to make objects compatible with their base type in situations where that base type is required .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM