Hero Image
- Mihai Surdeanu

The power of using interfaces in Java

The idea behind this article is quite simple: try to use interfaces instead of concrete classes whenever is possible!

Today, I'll try to convince you to use interfaces in Java as much as possible instead of concrete classes. The concept of interface in Java is quite powerful and comes with huge potential.

Let's start with a simple example, something already familiar if you are a Java developer:

ArrayList<Integer> myIntegers = new ArrayList<>();
myIntegers.add(1);
myIntegers.add(2);
myIntegers.add(3);
myIntegers.add(1);
// and so on

As you can already see, I just created a list of integers and I'll like to play with it. But, as you can see, I'm using concrete class to define variable type - myIntegers. If you want to pass this variable as argument to a method, you are allowed to define also concrete classes for method argument types. Going deeper, you can use ArrayList also for method returning type.

Unfortunately, this approach is not flexible. You should use interfaces as much as possible. Let me continue the example provided to let you understand why we should start to use interfaces. Let's assume, I have a simple method that is doing a mathematical sum:

public int sum(ArrayList<Integer> numbers) {
  return numbers.stream().reduce(0, Integer::sum);
}

If I want to pass myIntegers variable to sum method, everything will work as expected. Now, what happens if tomorrow there is a production issue reported and in my list I should not have duplicates?! There is a simple fix for my bug, right? I just need to switch from ArrayList to HashSet. Yeah, but there is not just a single line to be changed, because I need to change also method parameter type. Please do not duplicate sum method and create a new one for your desired type! The idea is to try to generalize as much as possible, to follow principle Open-closed principle from SOLID. So, if I dig a little bit more in Java Collection Framework, I'll discover a magic interface called Collectionwhich is generic and can be used as method parameter type. Having this in mind, I can easily refactor my sum method:

public int sum(Collection<Integer> numbers) {
  return numbers.stream().reduce(0, Integer::sum);
}

As a result, I can pass myIntegers variable to this new method signature. In fact, I can replace line ArrayList<Integer> myIntegers = new ArrayList<>(); with Collection<Integer> myIntegers = new ArrayList<>();. At this moment, I declare my variable as a simple collection and someone else who is going to use it, will not have to know anything about current implementation.

There are few cases when you want to restrict the type and in this situation, you can declare your variable ArrayList instead of List or Collection. If you want me to give you an example, you can think about a scenario when you want to enforce implementation type to ArrayList instead of LinkedListto avoid memory impact when you switch implementation type.

If you want, you can even go one step forward and use extends keyword to define a generic type Collection<? extends Number>and then you can pass Collection<Integer> or Collection<Short> to our sum method.

Happy coding!

Other Related Posts:

Despre Map-uri în Java

Despre Map-uri în Java

Salutare dragilor,

Acum câteva săptămâni am discutat despre mai multe implementări de liste în Java și am evidențiat câteva dintre cele mai importante asemănări și deosebiri. Astăzi, rămânem la capitolul colecții, dar vom vorbi despre Map-uri.

14th Feb 2022 - Mihai Surdeanu