Reputation: 1189
ArrayList aList = new ArrayList();
List aList = new ArrayList();
What's the difference between these two and which is better to use and why?
Upvotes: 18
Views: 5188
Reputation: 3429
This is a Java quirk, due to limited type inference and doctrine OOP. For local variables it's mostly style; if you need some specific feature of the subtype, use the subtype (examples below), but otherwise fine to use either.
Java style is to use the supertype, to enforce interfaces even within the body of implementations, and for consistency with visible types (Effective Java 2nd Edition: Item 52: Refer to objects by their interfaces). In languages with more type inference, such as C++/C#/Go/etc., you don't need to explicitly state the type, and the local variable will have the specific type.
For visible types (public or protected: fields, or parameters and return value of methods), you almost always want to use the most general type: interface or abstract class, to provide better flexibility (Effective Java: Item 40: Design method signatures carefully). However, for types that are not visible (private or package-private members, or local variables), it's ok to use either (it's just style) and sometimes necessary to use more specific types, including concrete classes.
See Effective Java for standard guidelines; personal thoughts follow.
The reason to use more general types even if not visible is to reduce noise: you're stating that you only need the more general type. Using general types on members that are not visible (like private methods) also reduces churn if you ever change types. However, this doesn't apply to local variables, where it's just changing one line anyway: ConcreteFoo foo = new ConcreteFoo();
to OtherConcreteFoo foo = new OtherConcreteFoo();
.
Cases where you do need the subtype include:
ensureCapacity
for ArrayList<T>
FakeFileSystem#createFakeFile
.As an example of the last, see Should I close a StringReader?: StringReader.html#close overrides Reader.html#close and does not throw an IOException
, so using StringReader
instead of Reader
for the local variable means you don't need to handle an exception that can't actually occur, and significantly reduces boilerplate.
Upvotes: 1
Reputation: 54302
List<T>
is interface, where ArrayList<T>
is class, and this class implements List<T>
interface.
I would prefer 2nd form, which is more general, ie if you do not use methods specific to ArrayList<T>
you can declare its type as interface List<T>
type. With 2nd form it will be easier to change implementation from ArrayList<T>
to other class that implements List<T>
interface.
EDIT:
As many of SO users commented both forms can be arguments to any method that accept List<T>
or ArrrayList<T>
. But when I declare method I would prefer interface:
void showAll(List <String> sl) ...
and use:
void showAllAS(ArrayList <String> sl) ...
only when my method uses method specific to ArrayList<T>
, like ensureCapacity()
.
Response with information that we should use typed List<T>
instead of just List
is very good (of course if we do not use ancient Java).
Upvotes: 5
Reputation: 45443
All these answers are the same recite from some dogma they read somewhere.
A variable declaration and initialization statement, Type x = new Constructor();
, is definitely part of implementation detail. It's not part of a public API (unless it's public final
, but List
is mutable so that's inappropriate)
As an implementation detail, who you are trying to fool with your abstractions? It's better to keep the type as specific as possible. Is it an array list or a linked list? Should it be thread safe or not? The choice is important for your implementation, you carefully chose the specific list impl. Then you declare it as a mere List
as if it doesn't matter and you don't care?
The only legitimate reason to declare it as List
is I'm too lazy to type. That also covers the argument that if I need to move to another List impl I have one less place to modify.
This reason is legitimate only when the variable scope is small, and you can see all of its usages from one glance. Otherwise, keep the most specific type, so that its performance and semantic characteristics are manifest across all the code that use the variable.
Upvotes: 2
Reputation: 1538
I know of at least one case when declaring a variable with an interface does not work. When you want to use reflection.
I made a bug fix on some code where I declared a variable as Map<String, MyObject>
and assigned it an instance of HashMap<String, MyObject>
. This variable was used as a parameter in a method call that was accessed via reflection. The problem is that reflection tried to find a method with HashMap
signature and not the declared Map
signature. Since there was no method with a HashMap
as a parameter I was unable to find a method by reflection.
Map<String, Object> map = new HashMap<String, Object>();
public void test(Map<String, Object> m) {...};
Method m = this.getClass().getMethod("test", new Class<?>[]{map.getClass()});
Will not find the method that uses the interface. If you make another version of test that uses HashMap
instead then it will work - but now you are forced to declare your variable with a concrete class and not the more flexible interface...
Upvotes: 0
Reputation: 2558
If you are using Java 1.4 or earlier, then I would use the second one. It's always better to declare your fields as generically as possible, in case you need to make it into something else later on, like a Vector, etc.
Since 1.5 I would go with the following
List<String> x = new ArrayList<String>();
It gives you a little bit of type safety. List 'x' can add a String Object, and that's it. When you get an item from the List 'x', you can count on the fact that a String is going to come back. This also helps by removing unnecessary casting, which can make code hard to read when you go back 6 months later and try to remember what your code does. Your compiler/IDE will help you to remember what type should be going in to List 'x' by displaying an error if you try to add any other Object type.
If you want to add multiple Object types to a List then you could add an Annotation to suppress the compile error
@SuppressWarnings("unchecked")
List y = new ArrayList();
Upvotes: 0
Reputation: 298908
Both are deprecated since Java 1.5.
It should be:
List<String> list = new ArrayList<String>();
// or whatever data type you are using in your list
Please read Effective Java by Joshua Bloch, especially these two items:
BTW, if you use Guava, you have a factory method for constructing an ArrayList so you don't have to repeat the type parameter:
List<String> list = Lists.newArraylist();
Upvotes: 7
Reputation: 182782
I prefer the second in most cases because it signifies that you're not using anything specific in the ArrayList api, and if you need to later you can substitute any other type of List without having to change any code except the first line.
Upvotes: 1
Reputation: 15767
List is an Interface, whereas ArrayList is an implementation of that interface.
The second is better because it means you can change your ArrayList for another implementation of List later without needing to change the rest of your application. You may want to do this for performance reasons, or because of other aspects of the behaviour of the List implementation that you have chosen/will choose.
Upvotes: 22