Introducing Function Pointers

Unlike other languages such as C, or JavaScript, Java doesn’t have a true function type. Meaning there is no type for a variable that a behaviour can be assigned to.\

For example, lets look at function pointers in TypeScript:

// Define a function type
type MyFuncType = (a: string) => string;
 
// Assign behaviours to varibles of type MyFuncType
// These variables are function pointers
const TransformUppercase: MyFuncType = (a) => a.toUpperCase();
const TransformLowercase: MyFuncType = function (a: string): string {
  return a.toLowerCase();
};
 
// A function that takes a "strategy" function and applies it
// This is known as a higher-order function (a function that takes a function as a parameter)
function passAFunctionIntoMe(func: MyFuncType, str: string): string {
  // Calling the function pointer
  const output = func(str);
  console.log(output);
}
 
// Passing the function pointer into the 'passAFunctionIntoMe' function
passAFunctionIntoMe(TransformUppercase, "hello");
passAFunctionIntoMe(TransformLowercase, "HI");
// Or, pass a behaviour directly as an argument
passAFunctionIntoMe((a) => a + "!", "hey" );
 
// ---- console output ----
// HELLO
// hi
// hey!
// ------------------------

Function Pointers in Java?

This is impossible in Java! In fact, Java doesn’t even have standalone functions in the same way JavaScript does, let alone function types. Apart from primitive types, everything else is an Object. Therefore, behaviours in Java can only exist as methods within a class or interface, not as standalone functions.

So how do we pass a Java method around as if it were a variable? We can achieve a similar effect by wrapping the method within an object that implements an interface. By doing this, the interface effectively acts as a “function type.”

class Main {
	public static void main(String[] args) {
		MyFuncInterface convertToUpper = new TransformUppercase();
		passAMethodIntoMe(convertToUpper, "hello");
		passAMethodIntoMe(new TransformLowercase(), "HI");
	}
	
	static void passAMethodIntoMe(MyFuncInterface func, String str) {
		String output = func.transform(str);
		System.out.println(output);
	}
}
 
interface MyFuncInterface {
	String transform(String a);
}
 
class TransformUppercase implements MyFuncInterface {
	@Override
	public String transform(String a) {
		return a.toUpperCase();
	}
}
 
class TransformLowercase implements MyFuncInterface {
	@Override
	public String transform(String a) {
		return a.toLowerCase();
	}
}
// -----stdout------
// HELLO
// hi
// -----------------

From the code above, we can see that we have used an Interface called MyFuncInterface with 1 abstract method to act as a sort of ‘function type’! From there, we can define named Classes which define the behaviour of the method transform, and pass instances of those Classes using polymorphism as a type of MyFuncInterface! This sort of mimics the behaviour of function types!

Anonymous Class!

The code above is verbose, and it doesn’t yet show how to pass a behaviour directly into a method as an argument, similar to the TypeScript example. Well… ever since Java 1.1, we have been able to do this using anonymous classes! This is a type of inner class that is defined and instantiated in a single statement and does not have a name.

class Main {
	public static void main(String[] args) {
		passAMethodIntoMe(
			new MyFuncInterface() {
				@Override
				public String transform(String a) {
					return a + "!";
				}
			},
		"hey");
	}
	
	static void passAMethodIntoMe(MyFuncInterface func, String str) {
		String output = func.transform(str);
		System.out.println(output);
	}
}
 
interface MyFuncInterface {
	String transform(String a);
}
// -----stdout------
// hey!
// -----------------

This may look like we are instantiating an Interface (which is impossible), but behind the scenes the Java compiler actually generates another Class which extends the MyFuncInterface. So this code compiles to 3 .class files! One for Main, one for the Interface and another for the anonymous Class!

From Java 8, anonymous functions (lambdas) were added as a language feature! This allows for single statement instantiation and definition of Interfaces like anonymous Classes, but no need for a compiler generated anonymous Class along with the much cleaner and denser lambda syntax.
Lambdas along with the other Functional Interfaces newly packaged in the standard library in a Java 8 pretty much lets the Java syntax appear like Java has a function type!
In fact the following syntax is valid!

Function<String,String> toLower = (x) -> x.toLowerCase();

Also, since Java 7, there is the MethodHandle Class that is actually a direct method reference to any method, constructor or field. The JVM links the MethodHandle to an actual method in-memory upon initialisation. This comes pretty close to function pointers in languages like C! But it is a lower-level Java API that most developers never have to touch. Lambdas and Method References in Java 8 provide a higher-level abstraction of MethodHandle