I cannot fathom why Java wishes to hide immutable unmodifiable collections from being passed around.
It makes life harder on devs because you can't guarantee that a class is returning anything immutable unmodifiable, forcing you to smatter calls to Collections.unmodifiableWhatever throughout your code. This is both wasteful and annoying.
Is there a reason I'm missing behind why you would make these methods protected, or another library I'm missing that contains public versions of immutable and/or unmodifiable collections?
For the purposes of learning more about java, let's say Scala does not count as an answer to "a library that contains public versions of immutable collections" :)
I think it has to do with the design goals of the framework:
The main design goal was to produce an API that was small in size and, more importantly, in "conceptual weight."
(Source)
You should check out Guava's immutable collection types, if you are willing to learn more conceptual weight :)
The Collections interface permits one to wrap an exiting Collection so that calls to mutator methods result in failure.
unmodifiableCollection(Collection c): "Returns an unmodifiable view of the specified collection."
It makes life harder on devs because you can't guarantee that a class is returning anything immutable, ... This is both wasteful and annoying.
The JDK source of the Collections.unmodifiableList() method is:
public static <T> List<T> unmodifiableList(List<? extends T> list) {
return (list instanceof RandomAccess ?
new UnmodifiableRandomAccessList<>(list) :
new UnmodifiableList<>(list));
}
I see why this can be considered wasteful (these generics also make it ugly). Why doesn't that method check whether the passed instance is already an UnmodifiableRandomAccessList or UnmodifiableList?
Related
I see that several converter methods in Java Library classes are non-static. Example : toEpochMilli() of Instant, toArray() of ArrayList. We can instead have static methods like toEpochMilli(Instant instant) and toArray(ArrayList arrayList) to achieve the same purpose, right? Is there any specific reason why these methods are non-static?
In a comment, you wrote:
Yes, it is more like an utility method and utility methods are usually static
which seems to reflect the mindset of your question very well. There are two problems with it. For one, there is no clear definition of “utility method”, which makes this categorization very subjective.
Second, the fact that utility methods are (or were) often implemented as static methods does not indicate that this is an actual design pattern that should be copied, just because it has been done that way before.
This is rather a historic compromise. Before Java 8, there were no default methods, so every method added to an interface had to be implemented, even if only by delegating to another helper method.
As a practical example, if sort was added to the List interface right at the beginning, every List implementor had to deal with it. So it was rather added as a static method to the Collections class which does not imply that anyone considered
Collections.sort(list, comparator); // no import static by that time
better than
list.sort(comparator);
Or that having a one-size-fits-them all implementation. Not only did this miss optimization opportunities for implementations like ArrayList, it also implied that erroneously applying it to an immutable list would stay unnoticed when the list happened to be in the right order.
Now that we have default methods, List has such a sort method, so implementors are still not required to deal with that, but they can override the method when it is appropriate. So ArrayList has an optimized version, immutable lists throw unconditionally and the implementation returned Collections.synchronizedList can make the entire operation synchronized.
The categorization as a “utility method” never played any role here. Note that other languages deal with it differently, e.g. via Extension Methods, which have their pros and cons on their own, but also show that having to write an invocation of a utility method as a static method is not an actual goal. Actually, it’s the opposite.
Static does not work as well with inheritance:
List<String> list1 = new ArrayList<>();
List<String> list2 = new LinkedList<>();
list1.size()
list2.size()
ArrayList.staticSize((ArrayList) list1)
LinkedList.staticSize((LinkedList) list2)
List.staticSize(list1); // NOT POSSIBLE
One would want something like:
interface List {
static staticSize(List list) {
return list.size();
}
}
But then still a non-static size() is used.
There is no static dispatch, polymorphism. (The JVM has some support, for other languages.)
I know what an interface is and what is a collection. But to be honest, I could not find any solid reason as why not to implement two collection interfaces in one class.
Got this question asked a few days back in an interview.
In some cases they are or can be implemented by the same object.
A Queue and a List are both implemented by LinkedList, TreeMap is both NavigableMap and SortedMap. There are a few other examples like this.
Each describes a trait or feature of the collection (exposed as a way to use it).
It just doesn't make sense all that often. For a Java collection to implement two interfaces it must be a near perfect implementation of both (and perhaps that's your answer).
A linkedlist COULD technically implement the methods of an ArrayList, however it would be a really bad idea.
Another point that hasn't been made already is that you can't implement the same interface twice with different type parameters.
As a result, if you tried to make a class implement both List<String> and Collection<Integer>, you would get a compiler error. This is because List<String> extends Collection<String> so we're trying to implement Collection twice with different type parameters.
If you did manage to implement two collection interfaces at once, it would have to be like the LinkedList example (where the generic type is the same when you think of it as a List and when you think of it as a Deque).
For starters, incompatible method contracts: e.g. List.add must always return true, and Set.add must always return false if the element is already present.
Is there any way to force an instance or a functional interface static method output to be inmutable in a fashion like Collections.immutable(x)?
I'd like for instance to create sort of Comparator functional interface and disallow chained operations like ".thenComparing()" for some of the static builder-like methods created instances.
You can force your return value to be immutable by returning an instance of an immutable class. There is no general purpose way to make instances of your class immutable without knowing what the class does.
The second paragraph of your question contains an incorrect assumption that .thenComparing(...) makes a comparator mutable. thenComparing leaves the original comparator intact and makes a new comparator.
Sadly, the Java collection types lack immutable collections, but google guava comes with lots of immutable collections (see https://code.google.com/p/guava-libraries/wiki/ImmutableCollectionsExplained), so you can use those as a return type.
Yet, I can't see how this correlates with the ability to compare something, since comparing doesn't change stuff.
Reading other people's code, I've seen a lot of:
List<E> ints = new ArrayList<E>();
Map<K, V> map = new HashMap<K, V>();
My question is: what is the point/advantage of instantiating them that way as opposed to:
ArrayList<E> ints = new ArrayList<E>();
HashMap<K, V> map = new HashMap<K, V>();
What also makes it odd is that I've never seen anything like:
CharSequence s = new String("String");
or
OutputStream out = new PrintStream(OutputStream);
Duplicates (of the first part of the question):
When/why to use/define an interface
Use interface or type for variable definition in java?
When should I use an interface in java?
why are interfaces created instead of their implementations for every class
What's the difference between these two java variable declarations?
Quick answer? Using interfaces and superclasses increases the portability and maintainability of your code, principally by hiding implementation detail. Take the following hypothetical example:
class Account {
private Collection<Transaction> transactions;
public Account() {
super();
transactions = new ArrayList<Transaction>(4);
}
public Collection<Transaction> getTransactions() {
return transactions;
}
}
I've declared a contract for an Account that states that the transactions posted to the account can be retrieved as a Collection. The callers of my code don't have to care what kind of collection my method actually returns, and shouldn't. And that frees me to change up the internal implementation if I need to, without impacting (aka breaking) unknown number of clients. So to wit, if I discover that I need to impose some kind of uniqueness on my transactions, I can change the implementation shown above from an ArrayList to a HashSet, with no negative impact on anyone using my class.
public Account() {
super();
transactions = new HashSet<Transaction>(4);
}
As far as your second question, I can say that you use the principal of portability and encapsulation wherever they make sense. There are not a terrible lot of CharSequence implementations out there, and String is by far the most used common. So you just won't see alot of developers declaring CharSequence variables in their code.
Using interfaces has the main advantage that you can later change the implementation (the class) without the need to change more than the single line where you create the instance and do the assignment.
For
List<E> ints = new ArrayList<E>();
Map<K, V> map = new HashMap<K, V>();
List and Map are the interfaces, so any class implementing those interfaces can be assigned to these references.
ArrayList is one of the several classes (another is LinkedList) which implement List interface.
Same with Map. HashMap, LinkedHashMap, TreeMap all implement Map.
It is a general principle To program for interfaces and not for implementations. Due to this, the programming task becomes easier. You can dynamically change the behavior of the references.
If you write
ArrayList<E> ints = new ArrayList<E>();
HashMap<K, V> map = new HashMap<K, V>();
ints and map will be ArrayList and HashMap only, forever.
Is a design principle that you program to the interface and not to the implementation.
That way you may provide later a new implementation to the same interface.
From the above link Eric Gamma explains:
This principle is really about dependency relationships which have to be carefully managed in a large app. It's easy to add a dependency on a class. It's almost too easy; just add an import statement and modern Java development tools like Eclipse even write this statement for you. Interestingly the inverse isn't that easy and getting rid of an unwanted dependency can be real refactoring work or even worse, block you from reusing the code in another context. For this reason you have to develop with open eyes when it comes to introducing dependencies. This principle tells us that depending on an interface is often beneficial.
Here, the termin interface refers not only to the Java artifact, but the public interface a given object has, which is basically composed of the methods it has, so, it could be a Java interface ( like List in your example ) or a concrete superclass.
So in your example if you ever want to use a LinkedList instead it would be harder because the type is already declared as ArrayList when just list would've been enough.
Of course, if you need specific methods from a given implementation, you have to declare it of that type.
I hope this helps.
#Bhushan answered why.
To answer your confusion Why nobody uses
CharSequence s = new String("String");
or
OutputStream out = new PrintStream(OutputStream);
CharSequence contains only few common methods. Other classes that implement this interface are mostly buffers and only String is immutable. CharSequence defines common api for classes backed by char array and This interface does not refine the general contracts of the equals and hashCode methods (see javadoc).
OutputStream is low-level api for writing data. Because PrintStream adds extra convenient methods for writing - higher level of abstraction, it's used over OutputStream.
You do this to make sure later when working with the variable you (or anyone using your classes) won't rely on methods specific for the implementation chosen (ArrayList, HashMap, etc.)
The reason behind this is not technical but the stuff you have to read between the lines of code: The List and Map examples says: "I'm only interested in basic list/map stuff, basically you can use anything here." An extreme example of that would be
Iterable<Foo> items = new ArrayList<Foo>();
when you really only want to do some stuff for each thing.
As an added bonus this makes it a little easier to refactor the code later into common utility classes/methods where the concrete type is not required. Or do you want to code your algorithm multiple times for each kind of collection?
The String example on the other hand is not seen wildly, because a) String is special class in Java - each "foo" literal is automatically a String and sooner or later you have to give the characters to some method which only accepts String and b) the CharSequence is really ahh minimal. It does not even support Unicode beyond the BMP properly and it misses most query/manipulation methods of String.
This (good) style of declaring the type as the Interface the class implements is important because it forces us to use methods only defined in the Interface.
As a result, when we need to change our class implementations (i.e. we find our ArraySet is better than the standard HashSet) we are guaranteed that if we change the class our code will work because both classes implement the strictly-enforced Interface.
It is just easier to think of String as of String. As well as it's easier (and more beneficial) to think of WhateverList as of List.
The bonuses are discussed many times, but in brief you simply separate the concerns: when you need a CharSequence, you use it. It's highly unlikely that you need ArrayList only: usually, any List will do.
When you at some point decide to use a different implementation, say:
List<E> ints = new LinkedList<E>();
instead of
List<E> ints = new ArrayList<E>();
this change needs to be done only at a single place.
There is the right balance to strike:
usually you use the type which gives you the most appropriate guarantees. Obviously, a List is also a Collection which is also something Iterable. But a collection does not give you an order, and an iterable does not have an "add" method.
Using ArrayList for the variable type is also reasonable, when you want to be a bit more explicit about the need for fast random access by object position - in a LinkedList, a "get(100)" is a lot slower. (It would be nice if Java had an interface for this, but I don't think there is one. By using ArrayList, you disallow casting an array as list.)
List<E> ints = new ArrayList<E>();
If you write some code that deals only with List then it will work for any class that implements List (e.g. LinkedList, etc). But, if your code directly deals with ArrayList then it's limited to ArrayList.
CharSequence s = new String("String");
Manually instantiating a String object is not good. You should use string literal instead. I am just guessing the reason that you don't see CharSequence might because it's quite new and also, strings are immutable.
This is programming to the interface not the implementation, as per the Gang of Four. This will help to stop the code becoming dependent on methods that are added to particular implementations only, and make it easier to change to use a different implementation if that becomes necessary for whatever reason, e.g. performance.
Collections.unmodifiableList(...) returns a new instance of a static inner class UnmodifiableList. Other unmodifiable collections classes are constructed same way.
Were these classes public, one had two advantages:
ability to indicate a more specific return value (such as UnmodifiableList), so an API user wouldn't come to the idea of modifying that collection;
ability to check during runtime if a List is instanceof UnmodifiableList.
So, were there any advantages not to make those classes public?
EDIT: No definitely convincing arguments were presented, so I choose the most upvoted answer.
Personally I completely agree with you. At the core of the problem is that fact that Java's generics are not covariant, which, in turn, is because Java's collections are mutable.
It is not possible for Java's type system to codify a type that seems to have mutators is actually immutable. Imagine if we were to start designing some solution:
interface Immutable //marker for immutability
interface ImmutableMap<K, V> extends Map<K, V>, Immutable
But then ImmutableMap is a subclass of Map, and hence Map is assignable from ImmutableMap so any method which returns such an immutable Map:
public ImmutableMap<K, V> foo();
can be assigned to a Map and can therefore be mutated at compile time:
Map<K, V> m = foo();
m.put(k, v); //oh dear
So, you can see that the addition of this type has not actually prevented us from doing anything bad. I think for this reason a judgement was made that it did not have enough to offer.
A language like scala has declaration-site variance annotations. That is, you could specify a type as being covariant (and hence immutable) as Scala's Map is (actually it's covariant in its V parameter). Hence your API can declare whether its return type is mutable or immutable.
As another aside, Scala lets you declare intersection types so that you don't even need to create the ImmutableXYZ interface as a separate entity, you could specify a method to return:
def foo : XYZ with Immutable
But then scala has a proper type system, whereas Java does not
I think both advantages are there but are not that useful. The main problems remain the same: UnmodifiableList still is a List and thus all the setters are available and the underlying collections still are modifiable. Making the class UnmodifiableList public would add to the illusion of being unmodifiable.
The nicer way would be for the compiler to help, but for that the collection class hierarchies would have to changed a lot. E.g., the collection API of Scala is way more advanced in that respect.
A disadvantage would be the introduction of at least three additional classes / interfaces into the API. Because of them not being that useful, I think leaving them out of the API is a good choice.
If it important for you to check if the list was created with Collections.unmodifiableList then you can create an instance and ask for the class. Now you you can compare this class with the class of any list.
private static Class UNMODIFIABLE_LIST_CLASS =
Collections.unmodifiableList( new ArrayList() ).getClass();
...
if( UNMODIFIABLE_LIST_CLASS == listToTest.getClass() ){
...
}
The answer to the why is quite simple: at the time, in 1998, efficient design was a bit flanky. People thought about it it wasn't apparently a priority. But there was no true, deep thinking about it.
If you want to use such a mechanism, use Guava's ImmutableList/Set/Map/...
They are explicitly Immutable and a good practice when using that library is not to return a List for instance but an ImmutableList. So you will know that a List/Set/Map/... is immutable.
Example:
private final ImmutableList constants = ...;
public final ImmutableList<String> getConstants() {
return constants;
}
About the design itself of UnmodifiableXxx, one could have done the following:
public static final class UnmodifiableXxx implements Xxx { // don't allow extend
// static if inside Collections
UnmodifiableXxx (Xxx backend) { // don't allow direct instanciation
...
}
...
}
Suppose UnmodifiableList was a public class. I suspect that it would lull programmers into a false sense of security. Remember, UnmodifiableList is a view of a modifiable List. This means that the contents of an UnmodifiableList can still change via any changes made to its underlying List. A naive programmer may not understand this nuance and may expect instances of UnmodifiableList to be immutable.
ability to indicate a more specific return value (such as UnmodifiableList), so an API user wouldn't come to the idea of modifying that collection;
In a proper API, this should already be documented in the javadoc of the method returning the unmodifiable list.
ability to check during runtime if a List is instanceof UnmodifiableList.
Such a need indicates that the actual problem lies somewhere else. It's a flaw in the code design. Ask yourself, have you ever had the need to check if a List is an instance of ArrayList or LinkedList? Whether it's an ArrayList, LinkedList or UnmodifiableList is clearly a decision which is to be made during code write time, not during code run time. If you're encountering problems because you're attempting to modify an UnmodifiableList (for which the API developer may have very good reasions which should be already documented), then it's rather your own fault, not a runtime fault.
All with all, it makes no sense. The Collections#unmodifiableXXX(), synchronizedXXX() and checkedXXX() do in any way not represent concrete implementations. They are all just decorators which can be applied regardless of the underlying concrete implementation.
I think the answer is because the method form properly knows about the generics used and requires no extra programming to pass this information through, whilst the class form would require more messing about. The method form for unmodifiableMap has two floating generic arguments, which it maps to both the generic arguments of the return type and of the passed argument.
public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {
return new UnmodifiableMap<K,V>(m);
}