Reputation: 3323
The Interfaces:
package sandBox.ps.generics.compositePattern;
import java.util.Collection;
interface AnimalInterface {
String getID();
/*
* getAnimals can be:
* 1. AnimalInterface
* 2. Anything that implements/extends AnimalInterface (i.e. AnimalInterface or DogInterface)
*/
Collection<? extends AnimalInterface> getAnimals();
}
interface DogInterface extends AnimalInterface {
String getBreed();
}
The classes:
package sandBox.ps.generics.compositePattern;
import java.util.Collection;
import java.util.Collections;
class AnimalClass implements AnimalInterface {
private final String id;
private final Collection<? extends AnimalInterface> animals;
AnimalClass(final String id,
final Collection<? extends AnimalInterface> animals) {
this.id = id;
this.animals = animals;
}
@Override
public String getID() {
return this.id;
}
@Override
public Collection<? extends AnimalInterface> getAnimals() {
return this.animals;
}
}
class DogClass extends AnimalClass implements DogInterface {
private final String breed;
DogClass(final String id, final String breed) {
super(id, Collections.<AnimalInterface> emptyList());
this.breed = breed;
}
@Override
public String getBreed() {
return this.breed;
}
}
Testing the classes:
package sandBox.ps.generics.compositePattern;
import java.util.ArrayList;
import java.util.Collection;
public class TestClass {
public void testA() {
// Dog Collection (Child)
final DogInterface dog = new DogClass("1", "Poodle");
final Collection<DogInterface> dogCol = new ArrayList<DogInterface>();
dogCol.add(dog);
// Animal Collection of Dogs (Parent)
final AnimalInterface animal = new AnimalClass("11", dogCol);
final Collection<AnimalInterface> parent = new ArrayList<AnimalInterface>();
parent.add(animal);
// Animal Collection of Animals (Grand-Parent)
final AnimalInterface grandParent = new AnimalClass("11", parent);
// Get Dog
for (final AnimalInterface parents : grandParent.getAnimals()) {
/* I know the this code would work.
* My question is, is there anyway to do this without explicit casting
for (final AnimalInterface child : parents.getAnimals()) {
if (child instanceof DogInterface) {
System.out.println(((DogInterface)child).getBreed());
}
}
*/
/* HERE: This is the part I am trying to solve.
* Do I use extends or super for AnimalInterface
* Is there any option such that I don't need to do a casting of some type
*/
for (final DogInterface child : parents.getAnimals()) {
System.out.println(child.getBreed());
}
}
}
}
Questions:
for (final AnimalInterface child : parents.getAnimals()) { if (child instanceof DogInterface) { System.out.println(((DogInterface)child).getBreed()); } }
Upvotes: 1
Views: 220
Reputation: 11433
Yes, you can do it. You need to give AnimalInterface
a generic parameter that tells you what types of animals you get from getAnimals()
, and then you need to use AnimalInterface<DogInterface>
for animal
and AnimalInterface<AnimalInterface<DogInterface>>
for grandParent
.
The complete code is this:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
interface AnimalInterface<A extends AnimalInterface<?>> {
String getID();
/*
* getAnimals can be:
* 1. AnimalInterface
* 2. Anything that implements/extends AnimalInterface (i.e. AnimalInterface or DogInterface)
*/
Collection<A> getAnimals();
}
interface DogInterface extends AnimalInterface<DogInterface> {
String getBreed();
}
class AnimalClass<A extends AnimalInterface<?>> implements AnimalInterface<A> {
private final String id;
private final Collection<A> animals;
AnimalClass(final String id, final Collection<A> animals) {
this.id = id;
this.animals = animals;
}
@Override
public String getID() {
return this.id;
}
@Override
public Collection<A> getAnimals() {
return this.animals;
}
}
class DogClass extends AnimalClass<DogInterface> implements DogInterface {
private final String breed;
DogClass(final String id, final String breed) {
super(id, Collections.<DogInterface> emptyList());
this.breed = breed;
}
@Override
public String getBreed() {
return this.breed;
}
}
class TestClass {
public void testA() {
// Dog Collection (Child)
final DogInterface dog = new DogClass("1", "Poodle");
final Collection<DogInterface> dogCol = new ArrayList<DogInterface>();
dogCol.add(dog);
// Animal Collection of Dogs (Parent)
final AnimalInterface<DogInterface> animal = new AnimalClass<DogInterface>("11", dogCol);
final Collection<AnimalInterface<DogInterface>> parent = new ArrayList<AnimalInterface<DogInterface>>();
parent.add(animal);
// Animal Collection of Animals (Grand-Parent)
final AnimalInterface<AnimalInterface<DogInterface>> grandParent = new AnimalClass<AnimalInterface<DogInterface>>("11", parent);
// Get Dog
for (final AnimalInterface<DogInterface> parents : grandParent.getAnimals()) {
for (final DogInterface child : parents.getAnimals()) {
System.out.println(child.getBreed());
}
}
}
}
Upvotes: 0
Reputation: 4509
You can always use a visitor:
DogBreedVisitor dogBreedVisitor = new DogBreedVisitor();
for (final AnimalInterface child : parents.getAnimals()) {
child.visit(dogBreedVisitor);
}
Here is a very simple implementation:
interface AnimalInterface {
void visit(AnimalVisitor visitor);
}
interface DogInterface extends AnimalInterface {
String getBreed();
}
interface AnimalVisitor {
public void visit(DogInterface dog);
public void visit(AnimalInterface animal);
}
class DogBreedVisitor implements AnimalVisitor {
@Override
public void visit(DogInterface dog) {
System.out.println(dog.getBreed());
}
@Override
public void visit(AnimalInterface animal) {
// ignore
}
}
class DogClass implements DogInterface {
@Override
public String getBreed() {
return "my breed";
}
@Override
public void visit(AnimalVisitor visitor) {
visitor.visit(this);
}
}
class AnimalClass implements AnimalInterface {
@Override
public void visit(AnimalVisitor visitor) {
visitor.visit(this);
}
}
Upvotes: 2
Reputation: 7179
I'm afraid if you want to invoke a subclass method on a superclass reference, then casting is indeed necessary.
That said, I would tackle this a different way but perhaps re-architecting your program so that it isnt necessary at the TestClass level to know what sort of animal you're dealing.
For example, you could do something along the lines of
for (final AnimalInterface child : parents.getAnimals()) {
System.out.println(child.getType()); // or something similar
The point being that you could push the responsibility of knowing what to retur is to the actual classes.
Classes where "breed" was the natural answer could return the breed, for others it could return something else.
In this case you may not want every animal to have this method (getType) so you could also perhaps have a hasType (or whatever) first.
Finally, if you find you do at some point really need to determine the actual type in question you could further push that to another class that only does this determination, leaving is so that only 1 class has this knowledge.
I hope this all makes sense - it's a bit of a rambling answer...
Upvotes: 2