(1) List<?> myList = new ArrayList<?>();
(2) ArrayList<?> myList = new ArrayList<?>();
I understand that with (1), implementations of the List interface can be swapped. It seems that (1) is typically used in an application regardless of need (myself I always use this).
I am wondering if anyone uses (2)?
Also, how often (and can I please get an example) does the situation actually require using (1) over (2) (i.e. where (2) wouldn't suffice..aside coding to interfaces and best practices etc.)
For example you might decide a
LinkedList
is the best choice for your application, but then later decideArrayList
might be a better choice for performance reason.Use:
Instead of:
For reference:
(posted mostly for Collection diagram)
I use (2) if code is the "owner" of the list. This is for example true for local-only variables. There is no reason to use the abstract type
List
instead ofArrayList
. Another example to demonstrate ownership:I think the people who use (2) don't know the Liskov substitution principle or the Dependency inversion principle. Or they really have to use
ArrayList
.Yes. But rarely for a sound reason (IMO).
And people get burned because they used
ArrayList
when they should have usedList
:Utility methods like
Collections.singletonList(...)
orArrays.asList(...)
don't return anArrayList
.Methods in the
List
API don't guarantee to return a list of the same type.For example of someone getting burned, in https://stackoverflow.com/a/1481123/139985 the poster had problems with "slicing" because
ArrayList.sublist(...)
doesn't return anArrayList
... and he had designed his code to useArrayList
as the type of all of his list variables. He ended up "solving" the problem by copying the sublist into a newArrayList
.The argument that you need to know how the
List
behaves is largely addressed by using theRandomAccess
marker interface. Yes, it is a bit clunky, but the alternative is worse.The "how often" part of the question is objectively unanswerable.
Occasionally, the application may require that you use methods in the
ArrayList
API that are not in theList
API. For example,ensureCapacity(int)
,trimToSize()
orremoveRange(int, int)
. (And the last one will only arise if you have created a subtype of ArrayList that declares the method to bepublic
.)That is the only sound reason for coding to the class rather than the interface, IMO.
(It is theoretically possible that you will get a slight improvement in performance ... under certain circumstances ... on some platforms ... but unless you really need that last 0.05%, it is not worth doing this. This is not a sound reason, IMO.)
That is a valid point. However, Java provides better ways to deal with that; e.g.
If you call that with a list that does not implement
RandomAccess
you will get a compilation error.You could also test dynamically ... using
instanceof
... if static typing is too awkward. And you could even write your code to use different algorithms (dynamically) depending on whether or not a list supported random access.Note that
ArrayList
is not the only list class that implementsRandomAccess
. Others includeCopyOnWriteList
,Stack
andVector
.I've seen people make the same argument about
Serializable
(becauseList
doesn't implement it) ... but the approach above solves this problem too. (To the extent that it is solvable at all using runtime types. AnArrayList
will fail serialization if any element is not serializable.)Somebody asked this again (duplicate) which made me go a little deeper on this issue.
If we use a bytecode viewer (I used http://asm.ow2.org/eclipse/index.html) weĺl see the following (only list initialization and assignment) for our list snippet:
and for alist:
The difference is list ends up calling INVOKEINTERFACE whereas aList calls INVOKEVIRTUAL. Accoding to the Bycode Outline Plugin reference,
while invokevirtual
In summary, invokevirtual pops objectref off the stack while for invokeinterface
If I understand this correctly, the difference is basically how each way retrieves objectref.
It is considered good style to store a reference to a
HashSet
orTreeSet
in a variable of type Set.Set<String> names = new HashSet<String>();
This way, you have to change only one line if you decide to use a
TreeSet
instead.Also, methods that operate on sets should specify parameters of type Set:
public static void print(Set<String> s)
Then the method can be used for all set implementations.
In theory, we should make the same recommendation for linked lists, namely to save LinkedList references in variables of type List. However, in the Java library, the List interface is common to both the
ArrayList
and theLinkedList
class. In particular, it has get and set methods for random access, even though these methods are very inefficient for linked lists.You can’t write efficient code if you don’t know whether random access is efficient or not.
This is plainly a serious design error in the standard library, and I cannot recommend using the List interface for that reason.
To see just how embarrassing that error is, have a look at the source code for the
binarySearch
method of the Collections class. That method takes a List parameter, but binary search makes no sense for a linked list. The code then clumsily tries to discover whether the list is a linked list, and then switches to a linear search!The
Set
interface and theMap
interface, are well designed, and you should use them.