0

I have classes similar to those shown below, and need to instantiate a Container object as on line (*).

public abstract class Foo {
    ...
}

public class Bar extends Foo {
    ...
}

public class Container {
    public Container(ArrayList<Foo> fooList) {
        this.fooList = fooList;
    }
    ...
    private ArrayList<Foo> fooList;
}

public static void main(String[] args) {
    ArrayList<Bar> barList = new ArrayList<Bar>();
(*) Container container = new Container(barList);
}

However, I get the following compilation error:

error: incompatible types: ArrayList<Bar> cannot be converted to ArrayList<Foo>

Is there a workaround to this? I need Container to be able to take ArrayLists of different subclasses that extend Foo.

2 Answers2

0

Change your Container class and main() method as shown below.

class Container {
    public Container(List<Foo> fooList) {
        this.fooList = fooList;
    }
    private List<Foo> fooList;
}
public class Solution {
    public static void main(String[] args) {
        List<Foo> barList = new ArrayList<>();

        barList.add(new Bar());

        Container container = new Container(barList);
    }
}
pcsutar
  • 1,715
  • 2
  • 9
  • 14
0

If on whatever reason you cannot change the Container class, the only solution would be:

public static void main(String[] args) {
    ArrayList<Foo> barList = new ArrayList<Foo>();
    barList.add(new Bar());
    Container container = new Container(barList);
}

However it's a rather bad design for three reason:

  1. Container class enforces usage of ArrayList over other implementations of List interface
  2. Container class enforces usage of list of Foo instances and doesn't accept lists of subclasses of Foo
  3. Container class can store any descedants of Foo and it is not configurable

First issue can be solved by changing ArrayList to List.

public class Container {
    public Container(List<Foo> fooList) {
        this.fooList = fooList;
    }
    private List<Foo> fooList;
}

Second issue can be solved by using Generics

public class Container {
    public Container(List<? extends Foo> fooList) {
        this.fooList = fooList;
    }
    private List<? extends Foo> fooList;
}

Last issue can be solved by parametrizing Container class itself:

public class Container<T extends Foo> {
    public Container(List<? extends T> fooList) {
        this.fooList = fooList;
    }
    private List<? extends T> fooList;
}
public static void main(String[] args) {
    ArrayList<Bar> barList = new ArrayList<>();
    Container<Foo> container = new Container<>(barList);
}

If you want to make sure that particular list implementation will have fast O(1) get by index operation you can only accept classes which implement both List and RandomAccess interfaces.

public class Container {
    public <L extends List<? extends Foo> & RandomAccess> Container(L fooList) {
        this.fooList = fooList;
    }
    private List<? extends Foo> fooList;
}
bedrin
  • 4,458
  • 32
  • 53