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?
As per the Java Language Specification, the methods in Java can return only one value at a time. So returning multiple values from a method is theoretically not possible in Java.
We can use Pair in Java to return two values. We can encapsulate all returned types into a class and then return an object of that class.
You can return multiple values from a function using either a dictionary, a tuple, or a list. These data types all let you store multiple values.
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.
I would suggest using instanceof. In fact this is exactly what the RandomAccess documentation suggests:
Generic list algorithms are encouraged to check whether the given list is an instanceof this interface before applying an algorithm that would provide poor performance if it were applied to a sequential access list, and to alter their behavior if necessary to guarantee acceptable performance.
Your constructor could potentially have two implementations. If RandomAccess is implemented then it stores a reference to the List otherwise it creates a new ArrayList and copies all elements to it:
class MyList {
private final int multiplier;
private final List<Integer> list;
public MyList(int multiplier, List<Integer> list) {
this.multiplier = multiplier;
if (list instanceof RandomAccess)
this.list = list;
else
this.list = new ArrayList<>(list);
}
public int get(int index) {
return multiplier * list.get(index);
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With