Search code examples
javalistcollectionsrandom-access

How to return an object with multiple types


Let's take an example to make it easier. I build a list which the constructor takes an integer and a List<Integer>. My list will contains all the elements of the given list multiplied by the integer. My list does not store the new elements but compute them on the fly:

class MyList extends AbstractList<Integer> implements RandomAccess {
    private final int multiplier;
    private final List<Integer> list;

    public MyList(int multiplier, List<Integer> list) {
        this.multiplier = multiplier;
        this.list = list;
    }

    @Override
    public Integer get(int index) {
        return list.get(index) * multiplier;
    }

    @Override
    public int size() {
        return list.size();
    }
}

Then we can call new MyList(3, list) with list = [0, 1, 2, 3] to get [0, 3, 6, 9].

I would like to limit the developer to give to the MyList constructor a list which is also RandomAccess, to be sure he will not ruin performances.

I tried to change the constructor with:

public <E extends List<Integer> & RandomAccess> MyList(int multiplier, E list)

MyList is not the issue but now we cannot invoke the constructor without using an implementation of both List<Integer> and RandomAccess like ArrayList<Integer>. So someone who have this list: List<Integer> list = new ArrayList<>(); cannot do new MyList(3, list); (Because it is declared with List<Integer> instead of ArrayList<Integer>).

The other solution I have is this one:

public MyList(int multiplier, List<Integer> list) {
        if(!(list instanceof RandomAccess)) {
            // Do something like log or throw exception
        }
        this.multiplier = multiplier;
        this.list = list;
    }

But now I cannot check at compile time if the list implements RandomAccess, and I need to use instanceof and I hate doing this.

I'm pretty sure there is a better way but what is it?


Solution

  • You could adopt the solution used by Collections.unmodifiableList. Instead of a public constructor, have a static method that returns one of two implementations, one implementing RandomAccess, the other not.

    Here is the code for Collections.unmodifiableList.

    public static <T> List<T> unmodifiableList(List<? extends T> list) {
        return (list instanceof RandomAccess ?
                new UnmodifiableRandomAccessList<>(list) :
                new UnmodifiableList<>(list));
    }
    

    I know you said you don't like using instanceof. Neither do I, but sometimes it's the best thing to do.

    Note that the solution using the constructor

    public <E extends List<Integer> & RandomAccess> MyList(int multiplier, E list)
    

    is not just ugly, in that it forces the programmer to cast (e.g. to an ArrayList), but it wouldn't actually work. For example, if list is an instance of Collections$UnmodifiableRandomAccessList, it would not even be possible to cast it to a type implementing both List and RandomAccess, because Collections$UnmodifiableRandomAccessList is private.