2
votes

My question largely relates to this one Is List<Dog> a subclass of List<Animal>? Why aren't Java's generics implicitly polymorphic?

So, say we have Animal that is a super interface of Cat and Dog. We also have a abstract class Litter such that

public abstract class Litter{

    public Litter(Collection<Animal> animals){}
}

And then we naturally have a concrete class KittyLitter

public class KittyLitter extends Litter{

    public KittyLitter(Collection<Cat> animals) {
        super(animals);
    }
}

...and puppy litter.

Naturally, we want to limit all Animal in a KittyLitter to just Cat. Why doesnt Java allow us to do this? Then, also lets say we add another method --

public abstract void addCub(Animal animal);

and concrete implementation in KittyLitter of

@Override
public void addCub(Animal cat) {
    // TODO Auto-generated method stub
}

At this point this breaks logic and allows us to insert a Dog into a KittyLitter which makes no sense. Any ideas as to why Java does these things to us? Also, if KittyLitter constructor can be changed to accept a List, why does the type argument behave differently? Can anyone explain why this is so?

EDIT: this is really not about constructors, but also any method that overrides.

2
What you have there, the constructors, will not compile.Sotirios Delimanolis
Yeah, can you clarify your question? Are you asking why Java doesn't let us do what you have in your constructors?Sotirios Delimanolis
Precisely. The way I have it in my question will not compile. Specifically the KittyLitter constructor wont compileaiguy
Generic types in Java are non-reified, erased, and invariant. For these reasons, Collection<Cat> is -NOT- a subtype of Collection<Animal>, so your code will not compile.scottb

2 Answers

5
votes

You need to make the superclass generic, using a bounded type parameter to say what kind of animals the litter can hold:

public abstract class Litter<T extends Animal> {  // <-- type bound
  public Litter(Collection<T> animals) { /* ... */ }
  public void addCub(T cub) { /* ... */ }
}

public class KittyLitter extends Litter<Cat> {
  public KittyLitter(Collection<Cat> cats) {
    super(cats);
  }
}

This allows the subclass to limit what kind of animals the inherited superclass methods will accept, by specifying a type for T. KittyLitter's addCub method takes a Cat argument, not an Animal. And PuppyLitter's addCub will take a Dog.

0
votes

Constructors are not overriding super constructors and you can declare them with any parameters you like. You can declare non-overriding methods with any parameters as well.

Overridden methods must be able to be called as if you call the parent directly so they cannot narrow parameter types. When Litter says it can be called with any Animal then it must be able to be called with any Animal.