Reputation: 8363
I need to have a List
that holds objects that extends class X, and implements interface Y.
I have tried this, but I realized it's not really what I need.
Consider this:
class Test<T extends MyBaseClass & MyBaseInterface>
{
public List<T> myList;
}
public abstract class MyBaseClass {}
public interface MyBaseInterface {}
public class MyDerivedClassA extends MyBaseClass implements MyBaseInterface {}
public class MyDerivedClassB extends MyBaseClass implements MyBaseInterface {}
Test<MyDerivedClassA> obj = new Test<MyDerivedClassA>();
obj.myList.add(new MyDerivedClassA()); // Works
obj.myList.add(new MyDerivedClassB()); // Compile error
This will make obj.myList
into a List<MyDerivedClassA>
at compile time. What I need is to have myList
hold a list of objects that can be of any class, provided it extends MyBaseClass
and implements MyBaseInterface
. It means that in the example, myList.add(new MyDerivedClassB())
should work.
This example is closer to what I need.
public abstract class Animal {}
public abstract class Mammal extends Animal {}
public abstract class Bird extends Animal {}
public class Tiger extends Mammal {}
public class Ostrich extends Bird {}
public interface RunningObject {}
public class RunningTiger extends Tiger implements RunningObject {}
public class RunningOstrich extends Ostrich implements RunningObject {}
public class RunningRobot extends NonAnimal implements RunningObject {}
// This line is syntactically wrong
public List<Animal & RunningObject> runningAnimals = new List<>();
runningAnimals.add(new RunningTiger()); // This should work
runningAnimals.add(new RunningOstrich()); // This should work
runningAnimals.add(new Tiger()); // This should give compile error
runningAnimals.add(new Ostrich()); // This should give compile error
runningAnimals.add(new RunningRobot()); // This should give compile error
What I need is to achieve type safety at compile time. This question is about Java and Generics, so please don't give me answer regarding things like design pattern (i.e. using Strategy design pattern).
Upvotes: 2
Views: 1542
Reputation: 3992
Introduce a "mid-level" abstract class:
class Test<T extends MyBaseClass & MyBaseInterface>
{
public List<T> myList;
}
abstract class MyBaseClass {}
interface MyBaseInterface {}
class MyDerivedClassA extends DerivedClass {}
class MyDerivedClassB extends DerivedClass {}
class DerivedClass extends MyBaseClass implements MyBaseInterface {}
You can then go with
Test<DerivedClass> obj = new Test<>();
obj.myList.add(new MyDerivedClassA()); // Works
obj.myList.add(new MyDerivedClassB()); // Works
Upvotes: 0
Reputation: 140319
If you can control access to the list, you can make the list simply hold instances of MyBaseClass
, but then only add to the list via a special method with the extra constraint:
private List<MyBaseClass> list;
<T extends MyBaseClass & MyBaseInterface> boolean add(T e) {
return list.add(e);
}
Now, you, as the author of the class, know that all of the instances in the list can be safely cast to MyBaseInterface
, as well as using them as MyBaseClass
directly.
This is quite janky, but it's fine to do casting of the elements if you must, provided you take responsibility for ensuring their safety - the compiler can only do so much.
Upvotes: 2