Interfaces

Declare APIs

Interfaces?

What is our problem?

  • Methods define behavior of classes
  • Switching between classes requires changing a lot of code
  • If both classes got the similar methods, nothing checks that the methods are really the same in terms of signature
  • When someone compiled code against your code, you cannot longer change things without breaking stuff in other people's code
  • Working in teams is hard
public class FastRandom
{
    public int getRandomInteger()
    {
        // code here
    }
}
public class SecureRandom
{
    public int randomInt()
    {
        // code here
    }
}
public class UseRandom
{
    private FastRandom random = new FastRandom();

    public int getRandomInteger()
    {
        int r = random.getRandomInteger();
    }
}

Switch Implementations

public class FastRandom
{
    public int getRandomInteger()
    {
        // code here
    }
}
public class SecureRandom
{
    public int randomInt()
    {
        // code here
    }
}
public class UseRandom
{
    public int getRandomInteger()
    {
        FastRandom random = new FastRandom();
        int r = random.getRandomInteger();
    }
}
public class UseRandom
{
  public int getRandomInteger()
  {
    SecureRandom random = new SecureRandom();
    int r = random.randomInt();
  }
}
public class UseRandom
{
    public int getRandomInteger()
    {
        final Random random =
            RandomFactory.getGenerator();
        int r = random.nextInt();
    }
}

Interfaces 101

What are Interfaces?

  • English: Template for blueprints aka classes
  • Compiler enforces correct classes when they implement interfaces
  • JVM expects the method to be there when calling the implementation
  • Use keyword implements
  • Method definition list
  • Says what has to be there
  • Defines the API aka the expected behavior
  • Methods and constants are permitted
  • Constants are ready to use and not a template
  • No instances from interfaces
  • No constructor definitions possible
  • Private not permitted
  • Static methods no possible
  • Method signature limits interface definitions somehow

Example with Interface

public interface Random
{
    // Initial seed to use for all generators
    public final int INITIALSEED = 42;

    /**
     * Returns a new random number
     *
     * @return a new random integer
     */
    public int nextInt();
}
public class FastRandom implements Random
{
    public int nextInt()
    {
        // code here

        // can use that here
        int a = INITALSEED;
    }
}
public class SecureRandom implements Random
{
    public int nextInt()
    {
        // code here

        // can use that here
        int a = INITALSEED;
    }
}
public class UseRandom
{
    public int getRandomInteger()
    {
        final Random random =
                RandomFactory.getGenerator();
        // random = new FastRandom();
        // random = new SecureRandom();

        int r = random.nextInt();

        // I can use that here.
        int foo = Random.INITALSEED;
    }
}

Annotation @Override

Support to see what comes from where

  • @Override annotation to support compiler error-checking
  • Helps to detect that a method is an implementation of an interface
  • hashcode() vs. hashCode()
  • If you don't implement something, you get an compiler violation!
  • But if you implement something else and you think it is an implementation of an interface... the compiler doesn't know.
  • Sadly @Override is used for abstract classes as well
public class FastRandom implements Random
{
    @Override
    public int nextInt()
    {
        // code here

        // can use that here
        int a = INITALSEED;
    }
}

Multiple Interfaces

Kill two birds with one stone

  • More than one interface can be implemented
  • Identical methods can only be implemented once
  • Collisions of methods cannot be resolved, such as return types
public interface Random
{
    public int nextInt();
}
public interface Resetable
{
    public void reset();
}
public class XRandom implements Random, Resetable
{
    @Override
    public void reset()
    {
        // code here
    }

    @Override
    public int nextInt()
    {
        // code here
    }

    public int moreStuff()
    {
    }
}

Extend Interfaces

Build on existing interfaces

  • Use extend to build on an interface and enrich it
  • You cannot change what is already declared, not hide, not remove anything
  • You can only add!
public interface Vehicle
{
    public String getLicensePlate();
}
public interface Car extends Vehicle
{
    public void stopEngine();
}
public interface Convertible extends Car
{
    public void closeTop();
}
public class SL500 implements Convertible
{
    private String plate;

    public SL500(String plate)
    {
        this.plate = plate;
    }

    public void stopEngine()
    {
        // code
    }

    public void closeTop()
    {
        // code
    }

    public String getLicensePlate()
    {
        return plate;
    }
}

Interfaces in Use

public interface Vehicle
{
    public String getLicensePlate();
}
public interface Car extends Vehicle
{
    public void stopEngine();
}
public interface Convertible extends Car
{
    public void closeTop();
}
public class Parking
{
    private Map<String, Vehicle> parkingSpaces = new HashSet<>();

    public void park(Car car)
    {
        parkingSpaces.add(car.getLicensePlate(), car);
        car.stopEngine();
    }

    public void park(Convertible convertible)
    {
        parkingSpaces.add(convertible.getLicensePlate(), convertible);
        convertible.closeTop();
        convertible.stopEngine();
    }

    public Vehicle retrieve(String plate)
    {
        return parkingSpaces.get(plate);
    }
}

Changing Interfaces

What can ruin your day

  • Any change will invalidate implementations of an interface
    • Adding
    • Renaming
    • Parameter changes
    • Removing
  • Yes, you can change comments and change variable names
  • Compile-time if you compile code that implements the interface
  • Compile-time if you use someone's interface and it changed
  • Runtime complains of you just use code that implements your interface

Ideas

How to permit interface evolution

  • Keep old interface unchanged
  • Extend your interface and create a new one
  • Create interface for newer functionality
  • Does not break anything, makes new functionality optional
public interface Converter
{
    public String toString(int i);
}
public interface ConverterV2 extends Converter
{
    public String toString(long l);
}
public class IntConverter implements Converter
{
    public String toString(int anInteger)
    {
        // code here
    }
}
public class NumberConverter extends IntConverter implements ConverterV2
{
    public String toString(long anLong)
    {
        // code here
    }
}

Java 8 and Interfaces

A way to change interfaces

  • default keyword to add implementations to avoid breaking changes
  • You still cannot remove or rename of course, but you can add.
  • Default-methods are limited in scope due to missing access to any states
public interface Converter
{
    public String toString(int i);

    public default String toString(long l);
    {
        // code here
    }
}
  • Interfaces can have static methods
  • These don't become an interface method to be overwritten
  • Rather a place for utility methods to avoid extra utility classes
public interface Converter
{
    public String toString(int i);

    public static boolean isInteger(int i)
    {
        // code here
    }
}

Best Practices

What you might want to consider first

  • Don't build interfaces for just having them
  • Interfaces become law when it is not just your code
  • Start to use public even though it is not needed in Java 8, but will be in Java 9
  • Naming: No conventions are given by Java
  • Suggestion: "it's typically preferable to have interfaces define capabilities not types"
  • So avoid IConverter, instead use Converter and later types, such as StringConverter
  • Also don't name classes ConverterImpl, because that indicates that you only have one and an interface was probably not needed