You're tripping over the fact that Java generics are not polymorphic on the type parameter.
Talking through your code fragment, let's pull the example apart:
protected List<GreyHound> greyHounds; // List<GreyHound> is fine
/** This method returns a lovely List of GreyHounds */
public List<GreyHound> getGreyHounds() {
return this.greyHounds;
}
/** Here is the problem. A List<GreyHound> is not a List<Dog> */
public List<Dog> getDogs() {
return getGreyHounds(); //compiler error
}
So your original comment is correct. The two Lists are definitely different with no inheritance between them. So, I would suggest that you investigate these two options:
Try returning a new list as you suggest in your comment. For example, return new ArrayList<Dog>(this.greyHounds);
Do you really need to keep a list of a specific breed of Dog? Perhaps you should define the data member to be a List<Dog>
to which you add your specific GreyHounds. I.e., protected List<Dog> greyHoundsOnly;
where you manage which dogs are allowed in the kennel via the object's external interface.
Unless you have a good reason to keep a type-specific list, I would think seriously about option 2.
EDIT: fleshing out my suggested options above:
Option 1: Return a new list. Pros: Simple, straightforward, you get a typed list out and it eliminates a thread-safety problem (doesn't expose an internal reference to the world). Cons: seemingly a performance cost.
// Original code starts here.
public interface DogKennel {
public List<Dog> getDogs();
}
public class GreyHoundKennel implements DogKennel {
protected List<GreyHound> greyHounds;
public List<GreyHound> getGreyHounds() {
return this.greyHounds;
}
// Original code ends here
public List<Dog> getDogs() {
// This line eliminates the thread safety issue in returning
// an internal reference. It does use additional memory + cost
// CPU time required to copy the elements. Unless this list is
// very large, it will be hard to notice this cost.
return new ArrayList<Dog>(this.greyHounds);
}
}
Option 2: Use a different data representation. Pros: plays nicer with polymorphism, returns the generic list that was the original goal. Cons: it's a slightly different architecture which may not fit with the original task.
public abstract class DogKennel {
protected List<Dog> dogs = new ArrayList<Dog>();
}
public class GreyHoundKennel extends DogKennel {
// Force an interface that only allows what I want to allow
public void addDog(GreyHound greyHound) { dogs.add(greyHound); }
public List<Dog> getDogs() {
// Greatly reduces risk of side-effecting and thread safety issues
// Plus, you get the generic list that you were hoping for
return Collections.unmodifiableList(this.dogs);
}
}