JakeFromStateFarm
JakeFromStateFarm

Reputation: 371

Access a subclass method through an ArrayList of superclass objects?

I have a superclass(Animal) and two subclasses(Frog and Lion). In my private method below, I'm creating three Lions and one Frog. I am displaying all four of the Animal's weight in my first loop, and all of the Lion's names in the second loop. I get the correct output, but my teacher told me "there is no need for creating two ArrayLists in your main method." I'm guessing he is suggesting that I use my first ArrayList to access the getName() method of my Lion Subclass.

This is how i did it and it was working:

 import java.util.ArrayList;

    public class AnimalTest {

        public static void main(String[] args){
            ArrayList<Animal> animalList = createList();

            for(int i = 0; i < animalList.size(); i++){
                System.out.println(animalList.get(i).getWeight() + " lbs");
            } 

            ArrayList<Lion> lionList = createList();
            for(int j = 0; j < 3; j++){
                System.out.println(lionList.get(j).getName());
            }

        }

        private static ArrayList createList(){
            ArrayList<Animal> list = new ArrayList<Animal>();

            list.add(new Lion("Leo I", 530.00));
            list.add(new Lion("Leo II", 560.00));
            list.add(new Lion("Leo III", 590.00));
            list.add(new Frog("Freddie", 7.00));

            return list;
        }

    }

Does he want me to access my getName() function from my Lion class through the same ArrayList list? I was thinking I could do this with some casting, like so:

//ArrayList<Lion> lionList = createList();
for(int j = 0; j < 3; j++){
   System.out.println(animalList.(Lion)get(j).getName());
}

But this doesn't work.. I don't understand how I'm supposed to get to my Lion subclass's getName() method without creating a another ArrayList..

Here are the rest of my classes:

public class Animal {
    private String genus;
    private String species;
    private double weight;
    private boolean tail;

    public Animal(){}

    public Animal(String genus, String species){
        this.genus = genus;
        this.species = species;
    }

    public String getGenus(){
        return genus;
    }

    public String getSpecies(){
        return species;
    }

    public double getWeight(){
        return weight;
    }

    public void setWeight(double weight){
        this.weight = weight;
    }

    public boolean hasTail(){
        return tail;
    }

    public void setTail(boolean tail){
        this.tail = tail;
    }

}


public class Lion extends Animal {
    private String name;

    public Lion(){}

    public Lion(String name, double weight){
        super("Panthera", "Leo");
        this.name = name;
        setWeight(weight);
        setTail(true);
    }

    public String getName(){
        return name;
    }
}


public class Frog extends Animal {
    private String name;

    public Frog(){}

    public Frog(String name, double weight){
        super("Lithobates", "Catesbeianus");
        this.name = name;
        setWeight(weight);
        setTail(false);
    }

    public String getName(){
        return name;
    }
}

Upvotes: 1

Views: 4295

Answers (3)

Makoto
Makoto

Reputation: 106390

A parent class cannot access the fields or methods of a child class, since it has no knowledge of what it has implemented.

With that in mind, the logical thing to do is place what you need to access in the Animal class instead. This means that you're going to pull the name field up into the parent Animal class and access it there instead.

Then, you only require one ArrayList:

ArrayList<Animal> animalList = createList();
for(Animal animal : animalList) {
    System.out.println(animal.getWeight());
    System.out.println(animal.getName());
}

Further, two very important changes to your createList method need to happen:

  • Do not return a raw ArrayList. Ever. You need to type it correctly.

    public ArrayList<Animal> createList() { }
    
  • You should not care about it being an ArrayList or a List. It's preferable (although not required) to program to the interface of List instead.

    public List<Animal> createList() { }
    

    For bonus points, you could prevent mutations of the list either by making it immutable or using PECS:

    public List<? extends Animal> createList() { }
    

Upvotes: 1

Elliott Frisch
Elliott Frisch

Reputation: 201429

Your createList function is return a raw-type. Please, don't do that. Also, I recommend you program to the interface. But your second example lionList would seem to need a createLionList method. Something like,

private static List<Lion> createLionList() {
    List<Lion> list = new ArrayList<>();
    list.add(new Lion("Leo I", 530.00));
    list.add(new Lion("Leo II", 560.00));
    list.add(new Lion("Leo III", 590.00));
    return list;
}

Then we can modify the first createList to add it. Something like,

private static List<Animal> createList() {
    List<Animal> list = new ArrayList<>();
    list.addAll(createLionList());
    list.add(new Frog("Freddie", 7.00));
    return list;
}

In your main method, I would prefer a for-each loop. And formatted output. Something like,

List<Animal> animalList = createList();
for (Animal a : animalList) {
    System.out.printf("Weight: %d lbs, Name: %s", a.getWeight(),
            a.getName());
}

Upvotes: 1

ApproachingDarknessFish
ApproachingDarknessFish

Reputation: 14313

You have the right idea, you've just got the syntax for the cast wrong. Try:

((Lion)animalList.get(j)).getName()

Although if both frogs and lions can have names, it would make more sense to just put the getName() in the Animal class.

Upvotes: 0

Related Questions