Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does the best practice of 'programming to interfaces' apply to local variables?

I've been told that programming to interfaces for local variables is useless and should not be done as it only hurts performance and offers no benefit.

public void foo() {
    ArrayList<Integer> numbers = new ArrayList<Integer>();
    // do list-y stuff with numbers
}

instead of

public void foo() {
    List<Integer> numbers = new ArrayList<Integer>();
    // do list-y stuff with numbers
}

I feel like the performance hit is negligible but admittedly there's not much to gain by using List semantics of ArrayList. Are there strong reasons to go one way or the other?

like image 275
Matthew Avatar asked May 16 '12 22:05

Matthew


People also ask

Why is it good practice to use local variables whenever possible?

So, by using a local variable you decrease the dependencies between your components, i.e. you decrease the complexity of your code. You should only use global variable when you really need to share data, but each variable should always be visible in the smallest scope possible.

Should you always code to an interface?

Interfaces aren't a prerequisite for writing code that works, but they are incredibly important when you come to write code as part of a bigger application. The problem with application development is, things will change as the application evolves over time.

What Cannot be used for local variables?

Local variables can only be declared as public. Explanation: You cannot apply any modifier except final to a local variable. i.e. you cannot make them transient, volatile, static, public, and private.

What type of variable should be defined in an interface?

Explanation: variable defined in an interface is implicitly final and static. They are usually written in capital letters.


2 Answers

Take this class for example:

public class Tmp {
    public static void main(String[] args) {
        ArrayList<Integer> numbers = new ArrayList<Integer>();
        numbers.add(1);
    }
}

It compiles down to this:

Compiled from "Tmp.java"
public class Tmp extends java.lang.Object{
public Tmp();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   new #2; //class java/util/ArrayList
   3:   dup
   4:   invokespecial   #3; //Method java/util/ArrayList."<init>":()V
   7:   astore_1
   8:   aload_1
   9:   iconst_1
   10:  invokestatic    #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   13:  invokevirtual   #5; //Method java/util/ArrayList.add:(Ljava/lang/Object;)Z
   16:  pop
   17:  return

}

While this class:

public class Tmp {
    public static void main(String[] args) {
        List<Integer> numbers = new ArrayList<Integer>();
        numbers.add(1);
    }
}

compiles down to this:

Compiled from "Tmp.java"
public class Tmp extends java.lang.Object{
public Tmp();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[]);
  Code:
   0:   new #2; //class java/util/ArrayList
   3:   dup
   4:   invokespecial   #3; //Method java/util/ArrayList."<init>":()V
   7:   astore_1
   8:   aload_1
   9:   iconst_1
   10:  invokestatic    #4; //Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
   13:  invokeinterface #5,  2; //InterfaceMethod java/util/List.add:(Ljava/lang/Object;)Z
   18:  pop
   19:  return

}

You'll see that the only difference is the first one (using an ArrayList) makes a call to invokevirtual and the other one (using List uses) invokeinterface. invokeinterface is actually a hair slower than invokevirtual (~38% according to this guy). This is apparently due to optimizations the JVM can make when searching through a concrete class's virtual method tables versus an interface's method tables. So what you're saying is actually true. Interface invocations are slower than concrete class invocations.

However, you have to consider what sorts of speed we're talking about. For 100,000,000 invocations, the actual difference was .03 seconds. So you have to have a ton of invocations to actually have a significant decrease in speed.

On the other hand, as @ChinBoon points out, coding to an interface makes it significantly easier for those using your code, especially if your code returns some sort List. So in the vast majority of cases, the ease of coding far outweighs the relative performance expense.


Added after reading @MattQuigley's comment and giving it a think on the drive home

What all this means is that this isn't something you should worry about too much. Any performance gain or penalty is likely to be very small.

Keep in mind, using the interface for your return types and your parameters on methods is a Very Good Idea. That allows you and anybody who uses your code to use whatever implementation of List best suits their needs. My example also shows that, if you happen to use a method that returns List, 99% of the time, you are not better off casting it to a concrete class just to get a performance boost. The time it takes to cast will likely outweigh the gain in performance.

That being said, this example also shows that, for a local variable, you are indeed better off using a concrete class rather than an interface. If the only methods you use are methods on List, then you can switch out implementing classes with no side effects. Plus you would have access to the implementation specific methods should you need them. Plus there is a minor performance boost.

tl;dr

Always use interfaces for return types and parameters on methods. It's a good idea to use concrete classes for local variables. It gives minor performance gains and there is no cost to switching implementations if the only methods you use are on the interface. In the end, you shouldn't worry about it too much. (Except the return types and parameters thing. That's important.)

like image 150
Tim Pote Avatar answered Sep 20 '22 08:09

Tim Pote


Does the best practice of 'programming to interfaces' apply to local variables?

Even though the scope of such variable is smaller than the scope of a member variable, most of the benefits for coding against interfaces when it comes to member variables apply also to local variables.

Unless you need ArrayList specific methods, go for a List.

I feel like the performance hit is negligible but admittedly there's not much to gain by using List semantics of ArrayList

I have never heard that invocations of interface methods would be slower than invoking methods on a class type.

Using ArrayList instead of List as static type sounds like premature optimization to me. As usual, favor readability and maintainability over optimizations until you've profiled your program as a whole.

like image 23
aioobe Avatar answered Sep 24 '22 08:09

aioobe