Reputation: 140427
This is basically a follow on to this question from yesterday. I tried to implement "the most generic way" (I could think of) in order to build "really" un-modifiable collections. The code below works fine; but it requires to suppress warnings ("rawtypes" and "unchecked").
I tried all kinds of things to avoid those warnings; but simply could not find an alternative. So, my question is: is there a clean way to avoid the warnings that I am suppressing?
( I saw this other question; but I am not 100% sure if it really applies to my code; as I am not sure if I could write down the "Generator" interface without using the raw type there).
private interface Generator<T> {
T generateNewInstance();
}
@SuppressWarnings("rawtypes")
private final static Generator ListGenerator = new Generator<List>() {
@Override
public List generateNewInstance() {
return new ArrayList();
}
};
public static <T> List<T> unmodifiableListBasedOnCloneOf(List<T> elements) {
@SuppressWarnings("unchecked")
List<T> newInstance = makeNewInstanceOf(elements.getClass(), ListGenerator);
newInstance.addAll(elements);
return Collections.unmodifiableList(newInstance);
}
private static <T> T makeNewInstanceOf(Class<T> incomingClass, Generator<T> fallbackGenerator) {
try {
return (T) incomingClass.newInstance();
} catch (IllegalAccessException | InstantiationException e) {
return fallbackGenerator.generateNewInstance();
}
}
Upvotes: 1
Views: 2280
Reputation: 140319
If you make a copy of the List<T>
into a new ArrayList<T>
, and pass that reference inline to Collections.unmodifiableList
, nothing other than the unmodifiable decorator has access to the copy to modify it:
List<String> original = new ArrayList<>(Arrays.asList("Hello", "World"));
// This simply decorates original, so updates to original are
// reflected in this list.
List<String> unmodifiableDecorator = Collections.unmodifiableList(original);
// The reference to the new ArrayList is scoped to the call to
// Collections.unmodifiableList, so nothing else can have a
// reference to it.
List<String> unmodifiableCopy =
Collections.unmodifiableList(new ArrayList<>(original));
System.out.println("Before clear:");
System.out.println(original);
System.out.println(unmodifiableDecorator);
System.out.println(unmodifiableCopy);
original.clear();
System.out.println("After clear:");
System.out.println(original);
System.out.println(unmodifiableDecorator);
System.out.println(unmodifiableCopy);
Output:
Before clear:
[Hello, World]
[Hello, World]
[Hello, World]
After clear:
[]
[]
[Hello, World]
So unmodifiableCopy
isn't modified, and can't be modified unless you can somehow convince the unmodifiableList
to give the delegate reference to you.
Since you can't modify it, you don't need to make the copy the same concrete list type as the input list. Just make it an ArrayList
, since it has pretty much the best read performance that can be achieved with a List
implementation.
So your method, without warnings or extra classes, can be:
public static <T> List<T> unmodifiableListBasedOnCloneOf(List<T> elements) {
return Collections.unmodifiableList(new ArrayList<>(elements));
}
Upvotes: 4
Reputation: 32831
It's not always possible to avoid a @SuppressWarnings annotation. I try to limit its scope to a local variable.
I came up with this:
private interface Generator<T> {
T generateNewInstance();
}
private static class ListGenerator<T> implements Generator<List<T>> {
@Override
public List<T> generateNewInstance() {
return new ArrayList<>();
}
};
public static <T> List<T> unmodifiableListBasedOnCloneOf(List<T> elements) {
@SuppressWarnings("unchecked")
Class<List<T>> clazz = (Class<List<T>>)elements.getClass();
List<T> newInstance = makeNewInstanceOf(clazz , new ListGenerator<T>());
newInstance.addAll(elements);
return Collections.unmodifiableList(newInstance);
}
private static <T> T makeNewInstanceOf(Class<T> incomingClass, Generator<T> fallbackGenerator) {
try {
return (T) incomingClass.newInstance();
} catch (IllegalAccessException | InstantiationException e) {
return fallbackGenerator.generateNewInstance();
}
}
Upvotes: 3