Reputation: 2331
I want to copy a List<T>
to another list without losing the runtime type. All of the techniques that come to my mind is not achieving this.
(Also, I don't want to simply return the reference of the input
list as I don't want edits in input
list to be reflected into the copied list)
What I tried (see all the copy*
methods) is listed below. As expected, none of them is giving (x.getClass() == y.getClass())
to be true
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;
public class TestX {
public static void main(String[] args) {
List<Integer> x = new LinkedList<>();
x.add(1);
x.add(2);
x.add(3);
List<Integer> y = copy2(x);
System.out.println("is runtime type equal? " + (x.getClass() == y.getClass()));
// I want (x.getClass() == y.getClass()) to be true
y.add(4);
System.out.println(x); // should be [1, 2, 3]
System.out.println(y); // should be [1, 2, 3, 4]
}
private static <T> List<T> copy0(List<T> input) {
return new ArrayList<>(input);
}
@SuppressWarnings("unchecked")
private static <T> List<T> copy1(List<T> input) {
Object[] x = input.toArray();
return (List<T>) List.of(x);
}
private static <T> List<T> copy2(List<T> input) {
return input.stream().collect(Collectors.toList());
}
private static <T> List<T> copy3(List<T> input) {
// Since Java 10
return List.copyOf(input);
}
}
Upvotes: 1
Views: 248
Reputation: 387
Try like this, the code shows below ignore try... catch..
.
private static <T> List<T> copy2(List<T> input) {
List<T> instance = input.getClass().newInstance();
return input.stream().collect(Collectors.toCollection(() -> instance));
}
Upvotes: 0
Reputation: 201447
A LinkedList
is not an ArrayList
. If you want both types to be equal, then you might use reflection to invoke the default constructor of the actual class called in. Something like,
private static <T> List<T> copy0(List<T> input) {
Class<?> cls = input.getClass();
try {
List<T> al = (List<T>) cls.getConstructor(null).newInstance(null);
Iterator<T> iter = input.iterator();
while (iter.hasNext()) {
al.add(iter.next());
}
return al;
} catch (Exception e) {
}
return null;
}
Which outputs
is runtime type equal? true
when I test it with your code. Note this makes dangerous assumptions about constructors and is not the approach I would take in a real project.
Upvotes: 0
Reputation: 45319
The best approach is to make the caller send in a function that will create a new list of the same type. The alternative would be to use reflection, which would begin a stream of dangerous assumptions.
The following two are examples of copy implementations adapted from your code:
private static <T> List<T> copy0(List<T> input, Supplier<List<T>> newListCreator) {
List<T> newList = newListCreator.get();
newList.addAll(input);
return newList;
}
private static <T> List<T> copy2(List<T> input, Supplier<List<T>> newListCreator) {
return input.stream().collect(Collectors.toCollection(newListCreator));
}
And you can call either in this way:
List<Integer> y = copy2(x, LinkedList::new);
Upvotes: 1