Jai
Jai

Reputation: 8363

Java: List of objects that extends/implements two classes/interfaces

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.

Another Example

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

Answers (2)

Chris311
Chris311

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

Andy Turner
Andy Turner

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

Related Questions