Developing Java, you have always learned that its best to create an ArrayList by using the List interface as the type for the variable the list is stored in. Like this
List<String> myList = new ArrayList<String>();
However, by looking at a lot of the android examples that is included in the bundle, they have creat开发者_高级运维ed the list by using the Class.
ArrayList<String> myList = new ArrayList<String>();
Is there any reason why this is done? Is it faster, lighter or something to explicitly setting the Class?
I'd recommend to read Performance Myths, which explains which are the advantages and problems of defining a variable as List or ArrayList.
Personally, I believe that the thing "you have always learned" misses the point. This:
List<String> myList = new ArrayList<String>();
has very little maintenance benefit. If you want to change it to a different implementation, it's still only one line to change if you use the implementation type. However, a totally different matter is this:
public void process(List<String> list) {
Here, using the interface really matters because it allows people who call the method to use different implementations without having to change the method signature (which they may not be able to do).
In resource constrained environment like the phones that Android are designed for, it's preferrable to avoid using Interface, since it involves additional virtual function call.
While there may be a performance benefit, it is almost certainly tiny and an example of premature optimization.
A much more likely explanation is that the writer of the examples wants to keep you focussed on the thing he (or she) is trying to teach you. Keeping the variable and the object it references the same type is one less thing to think about. Published code examples is one kind of code when you are pretty sure you will never have to modify it, and in that case it's perfectly fine to use ArrayList as the variable type.
When compiling code that operates on List<> objects, the compiler must use interface calls, which care more expensive than regular virtual calls on concrete types.
Of course, in a piece of code where the compiler sees the object being instantiated and can prove that something that is declared to be a List<> is always an ArrayList<>, the compiler should be able to just emit a regular call instead of an interface call, but methods which are not inlined and operate on already instantiated objects would not benefit from this optimization.
The ubiquitously used optimization that speeds up virtual calls, namely inline caches (frequently Polymorphic Inline Caching or PIC, not to be confused with Position Independent Code), benefits from the observation that instances of only a single subclass are ever accessed through a variable of a certain declared type. In this scenario, after the code has been running instrumented for a while the JIT can optimistically guess that a List<> object will only ever be an ArrayList<>, generate a trap in case the guess was wrong, and fall through with the ArrayList<> call.
Modern processors execute the check very quickly (because they are superscalar and have good branch prediction) so you don't notice the cost of all those virtual call and single implementation interface calls. But it does make the VM work hard, instrumenting, generating and patching all that code.
For server software running in steady state on HotSpot it's irrelevant but for fast startup on a mobile device it might make a difference - I don't know how good is Google's VM.
A nice article about it by Dr. Cliff Click, Jr. (custom big iron hardware, hotspot-deived VM): http://www.azulsystems.com/blog/cliff-click/2010-04-08-inline-caches-and-call-site-optimization
And of course "inline caching" on wikipedia.
The Interface x = new Implementation()
pattern is a popular naive attempt at being abstract.
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 are we trying to fool with this "abstraction"? 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 implementation, we carefully chose the specific list impl. Then we declare it as a mere List as if it doesn't matter and we 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 we can see all of its usages from one glance. Otherwise, declare the most specific type, so that its performance and semantic characteristics are manifest across all the code that use the variable.
There's not really much difference, except that in the Android example they are using a much more explicit type. Perhaps there are Android methods that rely on receiving an ArrayList rather than just a List.
It is a well-known "Design Principle" about howto make a good design, "program to an interface, not an implementation". Like Michael Borgwardt said, maybe it doesn't care to use this principle with your local variables, but if you have associations between types, then it make sense to program to interfaces rather than implementation to use benefits of OOP. To implement to interfaces rather than impl allows dynamic polymorphic assignments in runtime. say,
interface IAa { say(); }
class A implements IAa { public void say() { .. says A ... } }
class B implements IAa { public void say() { .. says B ... } }
class App {
public void someFoo() {
IAa myA = new A();
myA.say(); // says A
myA = new B();
myA.say(); // says B
}
...
}
i don't think that, it would be different in Android programming :)
精彩评论