2

Note: This is the conceptual version of my question. My actual question about the application of these concepts is shown below this chunk.


I am trying to figure out when an object is considered an instance of its own class or the class's superclass. Consider the following superclass Animal and Subclass Seahorse:

import java.util.List;
import java.util.ArrayList;
public class Animal
{
    public static void main(String[] args)
    {
        Animal a= new Animal();
        /*1. This line prints a's class*/
        System.out.println(a.getClass());

        List<Animal> animals= new ArrayList<Animal>(1);
        animals.add(new Seahorse(1));
        //2. Let's see what happens here
        animals.get(0).printClass();

        for(Animal e : animals)
        {
            e.printClass(); //3 This is an overriden method
            //e.printThisClass(); Compile Time error;
            //4. Note this method only exists in subclass
        }
        Animal hiahiahia = new Seahorse();
        hiahiahia.printClass(); //5. Lets see what happens
        hiahiahia.printThisClass(); //6. 
    }
    public void printClass()
    {
    System.out.println("Animal");
    }
}

public class Seahorse extends Animal
{
    private int y;
    public Seahorse(int a)
    {
        this.y=a;
    }
    public void printClass()
    {
        System.out.println("Seahorse");
    }

    public void printThisClass()
    {
        System.out.println("Seahorse");
    }
}

And the outputs look like this:

class Animal  //1.
Seahorse      //2.
Seahorse      //3.
Seahorse      //5.

You can see that Although Seahorse Objects are stored in Animal Objects or Lists, Polymorphism caused the Seahorse methods to be called.

However, when I try to call a method whose name only exists in the subclass on an Seahorse object disguised as a Animal object, the compile throws an error.

QUESTION: Is it true that polymorphism only works when methods override? Is there any time when

 Animal animal = new Seahorse(1);

Causes animal to be seen as an animal only(not a seahorse)?

What's the difference between

 Animal animal = new Seahorse();
 animal.printThisClass();

and

((Seahorse)animal).printThisClass();

Finally, when should I use casting?


I am working on a 2d Game and created the following class:

public class Entity
{
}

and

public class Character extends Entity
{
  //With bunch of extra fields and methods
}

I also have a controller class that has two fields:

private List<Entity>;
private List<Character>;

I separate them because Entities(in this case arrows, bullets) only collide with characters, but not with other entities. Here's the problem: I also have a render method that draws the BufferedImage of all my Entities. Now in order for the graph to make sense, the Entities should be rendered in the ascending order of their Y values.(That is, from farthest to nearest). However, this requires all characters and entities to be put in the same collection...

The question is: Should I separate them or not? If I put them in the same List, wouldn't that cause confusion because you can't tell characters from Entities?

Thanks : )

Ptolemorphism
  • 101
  • 2
  • 11
  • 1
    I would highly recommend taking a step back and looking very close at `interface` - for example a `Cat` can `Collide`, be an `Animal`, `Renderable`, `Movable` and probably a bunch of other things. `interface` allows you to better describe the "intent" and mix and match combinations which, in most cases, you probably wouldn't be able to do with extension alone. This also means that `Cat` can reside in any collection used to manage any of the `interface`s it implements. With a little bit of clever use of Java 8's `Stream`s, you could have a master list of "stuff" which can be filter – MadProgrammer Jun 03 '17 at 00:47
  • 1
    Where as a `Rock` can `Collide` and is `Renderable`, but not much else :P. Wherever possible, you want to avoid trying to make assumptions about the underlying implementation of the object/entity – MadProgrammer Jun 03 '17 at 00:48
  • 1
    If you have to cast, you're doing it wrong. – duffymo Jun 03 '17 at 00:49
  • @MadProgrammer Are you suggesting that I make two lists for collision and rendering? (for example, the rendering list can have things that don't collide with anything, such as health bars) That would led to more than one loops. Wouldn't that decrease program efficiency? – Ptolemorphism Jun 03 '17 at 01:07
  • @duffymo Could you be a little more specific? Thanks. – Ptolemorphism Jun 03 '17 at 01:08
  • 1
    You either understand polymorphism or you don't. Casting is a sign that you did it wrong. You should have a common interface, with each implementer having their own flavor of implementation. You iterate over a List of interface reference types. When you call the common method, each concrete subtype does the right thing. – duffymo Jun 03 '17 at 01:10
  • 1
    I was thinking you have one list and use Java 8s `Stream` support to filter it for your needs. In most cases, you'd have a "update" pass, which does the movement, collision and other life cycle processing, then you do a seperate paint pass, because the update pass could change what gets painted and when, so you don't want to o them in a single pass – MadProgrammer Jun 03 '17 at 01:11
  • @MadProgrammer I understand it now... I do have a tick and render method. That makes more sense. Thank you! – Ptolemorphism Jun 03 '17 at 01:17
  • @duffymo So basically when I iterate over a List of a class/interface, in the loop I should only use the methods from that class or interface on objects in the List, is that correct? – Ptolemorphism Jun 03 '17 at 01:20
  • Please stop writing and saying "basically". That's what I said. – duffymo Jun 03 '17 at 01:21
  • @duffymo I was just trying to make sure I understood what you said. Glad I did. – Ptolemorphism Jun 03 '17 at 01:23
  • "I am trying to figure out when an object is considered an instance of its own class or the class's superclass." It's always both. The compiler only has access to what's in the declared type, so a supertype reference cannot invoke methods known only to a subtype. But the object knows its own type at runtime, and will invoke its own version of overridden methods even when invoked via a supertype reference. – Lew Bloch Jun 03 '17 at 01:46

1 Answers1

0

Answer to the First Question:

What's the difference between

Animal animal = new Seahorse(); animal.printThisClass(); and

((Seahorse)animal).printThisClass();

There is no difference. When you cast that animal to the Seahorse it allows you to access the methods of the Seahorse. For instance, if the Seahorse class had a method called blowBubble() if you did animal.blowBubble()(assuming animal is defined like above) it will give you an error because the Animal class does not have a method called blowBubble().

However, when you cast the animal variable to Seahorse by doing ((Seahorse)animal) you are saying I know this animal is a Seahorse so allow me to use Seahorse's methods.

Animal animal = new Seahorse();
animal.method();
((Seahorse)animal).method(); // calls the exact same method even if it was overriden

However, you'll notice if you do this

Animal animal = new Animal();
((Seahorse)animal).blowBubbles();
((Seahorse)animal).method();

Each time you try to cast it, it will throw you a ClassCastException because an Animal cannot be transformed into a Seahorse. But, because a Seahorse is always and animal, you can call any animal methods on a Seahorse instance.

Seahorse seahorse = new Seahorse();
seahorse.methodInAnimalClass();

For your next question, having two separate lists for characters and entities is entirely up to you. Since you need to do a simple sorting algorithm for determining who has the greatest or lowest y position it probably won't matter.

You can always create a method called getAllEntities() that returns a new List that combines the entities and characters and then sort over that list and render them.

Like, the comments said, you should also look at interfaces, but I don't recommend creating tons of interfaces. I think starting simple is the way to go, however, as your game becomes for advanced, you may want to look into implementing them.

retodaredevil
  • 1,261
  • 1
  • 13
  • 20
  • Understood. But if I want to put \\ Animal animal = new Seahorse() \\ or \\Seahorse seahorse = new Seahorse() into a List or List, would that make any difference? The first object cannot be put into the second List, right? Your suggestion of combining the lists sound viable, but can I convert List to List? Thanks. – Ptolemorphism Jun 03 '17 at 02:38
  • Right, you can't put an Animal into a list of Seahorses because it is not a Seahorse. The only difference in doing Animal animal = new Seahorse() or Seahorse seahorse = new Seahorse() is that if you want to access methods defined in the Seahorse class or add it to a list, or pass it to another method, you will have to cast it. You cannot cast a List to List but you can look here [link](https://stackoverflow.com/questions/189559/how-do-i-join-two-lists-in-java) After combining the lists you should have a List. – retodaredevil Jun 03 '17 at 02:45