Let's now create a list of ImmutableMelon via Collections.unmodifiableList() and the List.of() methods:
private static final ImmutableMelon MELON_1
= new ImmutableMelon("Crenshaw", 2000);
private static final ImmutableMelon MELON_2
= new ImmutableMelon("Gac", 1200);
private static final List<ImmutableMelon> LIST
= Collections.unmodifiableList(Arrays.asList(MELON_1, MELON_2));
private static final List<ImmutableMelon> LIST
= List.of(MELON_1, MELON_2);
So, is the list unmodifiable or immutable? The answer is immutable. Mutator methods will throw UnsupportedOperationException, and we cannot mutate the instances of ImmutableMelon.
Pay attention to the fact that impenetrable immutability should take into consideration Java Reflection API and similar APIs that have supplementary powers in manipulating code.
For third-party library support, please consider Apache Common Collection, UnmodifiableList (and companions), and Guava's ImmutableList (and companions).
In the case of Map, we can create an unmodifiable/immutable Map via unmodifiableMap() or the Map.of() methods.
But we can also create an immutable empty Map via Collections.emptyMap():
Map<Integer, MutableMelon> emptyMap = Collections.emptyMap();
Alternatively, we can create an unmodifiable/immutable Map with a single element via Collections.singletonMap(K key, V value):
// unmodifiable
Map<Integer, MutableMelon> mapOfSingleMelon
= Collections.singletonMap(1, new MutableMelon("Gac", 1200));
// immutable
Map<Integer, ImmutableMelon> mapOfSingleMelon
= Collections.singletonMap(1, new ImmutableMelon("Gac", 1200));
Moreover, starting with JDK 9, we can create an unmodifiable Map via a method named ofEntries(). This method takes Map.Entry as an argument, as in the following example:
// unmodifiable Map.Entry containing the given key and value
import static java.util.Map.entry;
...
Map<Integer, MutableMelon> mapOfMelon = Map.ofEntries(
entry(1, new MutableMelon("Apollo", 3000)),
entry(2, new MutableMelon("Jade Dew", 3500)),
entry(3, new MutableMelon("Cantaloupe", 1500))
);
Alternatively, an immutable Map is another option:
Map<Integer, ImmutableMelon> mapOfMelon = Map.ofEntries(
entry(1, new ImmutableMelon("Apollo", 3000)),
entry(2, new ImmutableMelon("Jade Dew", 3500)),
entry(3, new ImmutableMelon("Cantaloupe", 1500))
);
In addition, an unmodifiable/immutable Map can be obtained from a modifiable/mutable Map via JDK 10, the Map.copyOf(Map<? extends K,? extends V> map) method:
Map<Integer, ImmutableMelon> mapOfMelon = new HashMap<>();
mapOfMelon.put(1, new ImmutableMelon("Apollo", 3000));
mapOfMelon.put(2, new ImmutableMelon("Jade Dew", 3500));
mapOfMelon.put(3, new ImmutableMelon("Cantaloupe", 1500));
Map<Integer, ImmutableMelon> immutableMapOfMelon
= Map.copyOf(mapOfMelon);
By way of a bonus for this section, let's talk about an immutable array.
Question: Can I create an immutable array in Java?
Answer: No, you cannot. Or... there is one way to make an immutable array in Java:
static final String[] immutable = new String[0];
So, all useful arrays in Java are mutable. But we can create a helper class to create immutable arrays based on Arrays.copyOf(), which copies the elements and creates a new array (behind the scenes, this method relies on System.arraycopy()).
So, our helper class is as follows:
import java.util.Arrays;
public final class ImmutableArray<T> {
private final T[] array;
private ImmutableArray(T[] a) {
array = Arrays.copyOf(a, a.length);
}
public static <T> ImmutableArray<T> from(T[] a) {
return new ImmutableArray<>(a);
}
public T get(int index) {
return array[index];
}
// equals(), hashCode() and toString() omitted for brevity
}
A usage example is as follows:
ImmutableArray<String> sample =
ImmutableArray.from(new String[] {
"a", "b", "c"
});