How do I call a method on all objects in an ArrayList?
So, I have the interface Pet
shown below:
public interface Pet{
void Eat();
}
What is implemented:
public class Puppies implements Pet {
@Override
public void Eat() {
// Something gets eaten here, presumably
}
}
and
public class Kittens implements Pet {
@Override
public void Eat() {
// Everybody knows Kittens eats something different
}
}
Hope I did the following, create ArrayList
new pets:
public class PetList{
public PetList(){
ArrayList pets = new ArrayList<Pet>();
Puppies spot = new Puppies();
Puppies rex = new Puppies();
Kittens meowth = new Kittens();
pets.add(spot);
pets.add(rex);
pets.add(meowth);
}
public static void main(String[] args){
// No idea how to handle this bit
}
What I want to do next is go through and tell all my pets to eat. How should I do it?
source to share
The main problem with your current code is that ArrayList
( pets
) is a local constructorPetList
, which means you cannot access outside of the class constructor PetList
.
So, first make ArrayList
as an instance variable of the class PetList
so that it can be accessed via the object even outside the constructor .
Then you can provide a method eatAll()
that iterates ArrayList<Pet>
and calls the method eat()
on all objects pet
.
You can link to the code below and follow the inline comments:
public class PetList{
private List<Pet> pets;//now this is an instance variable
public PetList(){
this.pets = new ArrayList<Pet>();//this list is not local now
Puppies spot = new Puppies();
Puppies rex = new Puppies();
Kittens meowth = new Kittens();
pets.add(spot);
pets.add(rex);
pets.add(meowth);
}
public void eatAll() { //method to invoke eat() on list of pets
for(Pet pet : this.pets) { //iterate the pets list
pet.eat();//call eat() on each pet object
}
}
public static void main(String[] args){
PetList petList = new PetList();
petList.eatAll();//invoke the eatAll() method
}
}
As a side note, I highly recommend you follow the Java naming standards ( eat()
should be eat()
, that is, method names should start with lowercase) and consider renaming PetList
class toPetsTesting
(or whatever) to make it more intuitive for developers.
Last but not least, do not assign objects directly to specific class classes such as ArrayList<Pet> pets = new ArrayList<>();
or Puppies spot = new Puppies();
.
The best practice is that you need to assign objects to interface types List<Pet> pets = new ArrayList<Pet>();
or Pet spot = new Puppies();
(which is called code for interface types )
source to share
What is implemented:
In fact, you need to implement an interface, this is not enough to define methods in concrete classes. This is how you do it:
public class Kittens implements Pet {}
Since you have an interface, let the type of your instance be Pet, so instead of
Puppies spot = new Puppies();
which would be
Pet spot = new Puppies ();
I suggest exploring / continuing to learn the basics of Java (Inheritance, Polymorphism and other concepts).
Here's what you need to get it to work as you expect:
Pet.java
public interface Pet {
void eat();
}
Kettens.java
public class Kittens implements Pet {
@Override
public void eat() {
System.out.println("Everybody knows Kittens eats something different");
}
}
Puppies.java
public class Puppies implements Pet {
@Override
public void eat() {
System.out.println("Something gets eaten here, presumably");
}
}
PetList.java
import java.util.ArrayList;
import java.util.List;
public class PetList {
public static void main(String[] args) {
Pet spot = new Puppies();
Pet rex = new Puppies();
Pet meowth = new Kittens();
List<Pet> pets = new ArrayList<>();
pets.add(spot);
pets.add(rex);
pets.add(meowth);
PetList.feadPets(pets);
}
public static void feadPets(List<Pet> pets) {
for (Pet p : pets) {
p.eat();
}
// Java 8 / Stream syntax.
// pets.forEach(Pet::eat);
}
}
source to share
The problem with your current approach is ArrayList pets
only limiting the constructor, so main
cannot access it.
This is how you fix it
public class PetList {
List<Pet> pets; // should be private, though
public PetList() {
this.pets = new ArrayList<Pet>();
}
And in the main
// Should be using a getter, though
for (Pet pet : new PetList().pets) {
pet.eat();
}
Another suggestion
1) Create multiple List<Pet>
(or make your whole class itself a list)
2) Iterate it and call this shared interface method
public class PetList extends ArrayList<Pet> {
public PetList() {
add(new Puppy());
add(new Kitten());
}
public static void main(String[] args) {
PetList pets = new PetList();
for (Pet pet : pets) {
pet.eat();
}
}
}
source to share
javaguy provided the answer you may have been looking for, but since you seem to be new to this question, there are some more tips for your code:
If you want Java to recognize your class Puppies
as an implementation of your interface Pet
, you must explicitly state this in your class definition:
public class Puppies implements Pet
In your class, PetList
you initialize (i.e. create) your list and pets variables in a so called constructor. This means that these variables are not available unless you create an object of this class. You might want to find a guide on how to deal with installing classes. For now, I propose the following solution, instead of writing a subclass and initializing variables there, do it in your method main
:
import java.util.ArrayList;
public class PetList{
public static void main(String[] args){
//Initialize the pet list
ArrayList pets = new ArrayList<Pet>();
Puppies spot = new Puppies();
Puppies rex = new Puppies();
Kittens meowth = new Kittens();
pets.add(spot);
pets.add(rex);
pets.add(meowth);
To feed the animals, you can iterate over the list using a for-each-loop. (You should watch this too)
//Feed the animals
for(Pet p: pets){
System.out.println("Serving a meal.")
p.Eat();
}
[Please also read the last point in the style guide below, as this code will not compile.] Quick introduction: This construct iterates, i.e. loops through all the elements in a variable pets
and stores them, one at a time, in a local variable p
. Inside the brackets, you can perform any operation on this variable, in this case call a method on it Eat()
. See what the console outputs if you added methods System.out.…
and in your Eat()
add animalsSystem.out.println("Kittens just ate.");
Since you are using ArrayList
here, you can also access individual pets through their index, i.e. position in the list. Using a for loop:
//Feed the animals once more
for(int i=0; i<pets.size(); i++){ // For each i which is greater or equal to zero and less than the size of pets,
pets.get(i).Eat(); //Feed the animal in the i-th position in pets
}
Quick style guide
- Class names must be singular, eg.
Puppy
instead ofPuppies
. - Method names should not be capitalized, for example.
Eat()
insteadEat()
- You can specify
ArrayList
(and basically any other container) what data you will enter into it. This improves type safety because it can check for errors that will still be obvious if you run your code and it crashes.
Suppose you had another class for trees: public class Tree { /* No Eat() method! */}
Since your code is standing now, it would be possible, but fail:
Tree tree = new Tree();
pets.add(tree);
for(Pet p: pets){
p.Eat(); // Will crash if p reaches the tree.
}
But if you parameterize your PetList
like this
ArrayList<Pet> pets = new ArrayList();
Tree tree = new Tree();
pets.add(tree);
Java might detect the tree as odd to add to the list of pets. (Actually, Java won't compile the first instance, but I think this is a good approach to type safety.)
source to share