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