92. Referring to objects by their interface

An interface defines functionality rather than state. In many cases you just want to interact with a specific set of an object’s functionality and referring to the interface rather than a specific class can make your code more adaptable. Two interfaces, java.util.Map and java.util.List, come to mind when considering this:

  • java.util.Map is implemented in a range of classes:
    • java.util.Properties is used when reading and writing property files
    • java.util.LinkedHashMap retains the order in which keys are inserted (and is used when you def myMap = [:])
    • java.util.concurrent.ConcurrentHashMap provides for full concurrency in retrievals and updates
  • java.util.List
    • java.util.ArrayList provides a resizable array (and is used when you def myList = [])
    • java.util.Stack is a last-in-first-out (LIFO) stack

So whilst I might choose a specific implementation class for my variable because of a need such as speed or concurrency, other parts of my code may be more interested in the interface level (e.g. java.util.Map). When declaring a variable I can provide an interface as the data type, e.g. Map posts = [:]. This means that I don’t really care which implementation of Map is assigned to posts, I’ll only be accessing the methods declared by Map (e.g. keySet). In a slightly more convoluted example, Posts p = new Member() indicates that the variable p is only interested in the Member object’s Posts functionality.

Whilst handy for variable declaration, referring to interfaces when defining method parameters is extremely useful as it makes the method more abstract. Consider a method that will accept a Map parameter and iterate through it:

def displayKeys(Map m) {
    for (key in m.keySet()) {
        println key
    }
}

def myMap = [name: 'Bill', id: '1234']

displayKeys myMap

It’s a rather useless example but the point is that the displayKeys method can accept any value for parameter m, provided m is an instance of a class that implements the Map interface. If I’d been specific and written the method signature as def displayKeys(LinkedHashMap m), my method has a far narrower field of usage. As I’m just using the Map interface’s keySet method, I don’t really need to limit the possible values for m.

Obviously, if you want/need to constrain usage to a specific implementation, you would declare that as the data type for the parameter. If the method needed the storeToXML functionality of java.util.Properties then I’d need to use that instead of java.util.Map - just take a moment to make sure it’s really required.