Free Java Tutorials >> Table of contents >> Interfaces Lambdas

1. Interfaces

Multiple Inheritance

In Java, you cannot extend multiple classes. This avoids the "diamond problem" (sometimes called the "deadly diamond of death"), where B extends A, C extends A, and D extends both B and C. In that situation, it would become unclear which methods overwritten by both B and C get inherited by D. Java provides a construct called an interface instead:

An interface, like an abstract class, is a variable type that cannot itself be instantiated. It can be subclassed using the implements keyword. In addition, interfaces cannot have fields and all methods are abstract by default. There is no need for the abstract keyword. Instead, you are forced to provide no body for each method:

interface MyType {
	public void method1();
	public int method2(int arg1);
}
class MyClass implements MyType {
	public void method1() {
		//forced to write this method since superclass method is abstract
	}
	public int method2(int arg1) {
		//also forced to write this method
		return 0;
	}
}

Naming convention: Due to the fact that interfaces are purely a collection of method signatures, we often call interfaces "...able", such as "Comparable" "Runnable" "Serializable" "Iterable", etc. This naming convention is not always adhered to, but is useful at times.

Creating Generic Interfaces

Like classes and abstract classes, interfaces can be generic:

interface Doable<T> {  //Parametrized on type T
	public T doIt();
}
class MyClassOne implements Doable<String> {
	public String doIt() {
		//method forced to return String
		return "";
	}
}
class MyClassTwo<U> implements Doable<U> {
	public U doIt() {
		//You can allow subclass to also be generic
		return null;
	}
}

Implementing multiple interfaces

To implement more than once interface, separate each interface with a comma after the implements keyword:

interface A {}
interface B {}
class C implements A, B {}

2. Anonymous Classes and Lambas

Motivation for passing functions

Java, unlike some other languages, did not originally allow you to store a function in a variable. Instead, we use interfaces to simulate the ability of passing a function. An interface with just one method is used to represent the signature of the function. Then, a class implementing that interface represents the body of the function. Finally, an instance of that class can be passed around as a variable, and the method can be called to invoke the function.

Here we create to functions via the "SquareFunction" and "DoubleFunction" classes, both of which implement the "MyInterface" interface with method "doMath":

public class MyProgram {
    public static void main(String[] args) {
		MyInterface squareNumber = new SquareFunction();
		System.out.println(doFunctionTwice(squareNumber, 3));
		MyInterface doubleNumber = new DoubleFunction();
		System.out.println(doFunctionTwice(doubleNumber, 3));
    }
	public static int doFunctionTwice(MyInterface function, int number) {
		number = function.doMath(number);
		number = function.doMath(number);
		return number;
	}
}
interface MyInterface {
	public int doMath(int x);
}
class SquareFunction implements MyInterface {
	public int doMath(int x) {
		return x * x;
	}
}
class DoubleFunction implements MyInterface {
	public int doMath(int x) {
		return x * 2;
	}
}

The above method to pass functions is very verbose. In addition, writing the function definition in its own class separates the definition with where we actually use the code. We will combine both with an anonymous class.

Anonymous classes:

public class MyProgram {
    public static void main(String[] args) {
		//The following 5 lines are all one statement:
		MyInterface anonymousFunction = new MyInterface(){
			public int doMath(int x) {
				return x * x;
			}
		}; //statement ends
		System.out.println(doFunctionTwice(anonymousFunction, 3)); 
		
		//You can inline the anonymous function as well:
		System.out.println(doFunctionTwice(new MyInterface(){
			public int doMath(int x) {
				return x * 2;
			}
		}, 3)); //statement ends
    }
	public static int doFunctionTwice(MyInterface function, int number) {
		number = function.doMath(number);
		number = function.doMath(number);
		return number;
	}
}
interface MyInterface {
	public int doMath(int x);
}

In the above code, the anonymousFunction class definition is inlined with the variable declaration. Thus, we cannot refer to the class name anywhere else in the code, but it shortens our code. In addition, we do not even need to store this anonymous class in a variable, as we can pass it directly to another function. Much shorter!

Lambdas

An anonymous class is also sometimes called a "lambda", a "functor" or a "closure". Although each of these terms have their own specific meaning and context, programmers sometimes use them interchangeably.

Because lambdas are used so often, Java 8 introduced new syntax to make it even easier to write:

public class MyProgram {
    public static void main(String[] args) {
		//This is the pre-Java-8 style anonymous class:
		System.out.println(doFunctionTwice(new MyInterface(){
			public int doMath(int x) {
				return x * x;
			}
		}, 3)); 
		
		//That Java 8 Lambda:
		System.out.println(doFunctionTwice( (x) -> x * 2, 3)); //statement ends
    }
	public static int doFunctionTwice(MyInterface function, int number) {
		number = function.doMath(number);
		number = function.doMath(number);
		return number;
	}
}
interface MyInterface {
	public int doMath(int x);
}

Thus, we use the very short (x)->x*2 which actually compiles to the same thing as this:

new MyInterface(){
	public int doMath(int x) {
		return x * 2;
	}
}

Java is able to infer the function name because doFunctionTwice takes a MyInterface interface, and the only method in that interface is called doMath. In fact, you may only use lambdas with interfaces that only have one method.

Java is also able to infer the argument type (the function signature), so rather than typing (int x), the lambda simply has the variable name (x). Since the lambda must return a value, Java infers the return statement, so all you need to write is (x)->x*2. Lambda expressions can even infer generics from the context!

Previous: Abstract

Next: Common Interfaces