Method Handles in Java

Embed Size (px)

Citation preview

Method Handles in Javaand some neat Java 8 stuff too!

So what are we going to talk about?

What are method handles?

How are method handles related to reflection?

What is invokedynamic about?

How are method handles used with invokedynamic?

What are lambda expressions?

How are method handles used with lambda expressions?

What are a few other interesting things coming in Java 8?

What is a method handle?

A reference to a method (or field, constructor or other bit of functionality) that can be executed

Defined in package java.util.invoke

Introduced with invokedynamic support (JSR 292) in Java 7

How is a method handle obtained?

Every method has a type that is described by a MethodType instance

MethodHandle instances are obtained using a MethodHandles.Lookup factory that knows about methods that are available in the current context

The MethodType Class

Used to describe the parameters and return type of a method handle

All instances are immutable

Instances are created using the MethodType.methodType factory method

ExamplesMethodType.methodType(BigDecimal.class, int.class) (I)Ljava/math/BigDecimal;

MethodType.methodType(String.class) - ()Ljava/lang/String;

MethodType.methodType(boolean.class, String.class) - (Ljava/lang/String;)Z;

The MethodHandles.Lookup Class

Factory for creating method handles

Created using MethodHandles.lookup method

Knows about all methods that are accessible from the calling context

Method handles are obtained from the factory using the find methods which include:findVirtual instance methods

findConstructor - constructors

findStatic class methods

How is a method handle obtained? (continued...)

Obtain a lookup context

Create a MethodType that describes the return type and parameters of the method

Get a MethodHandle instance representing the method using the appropriate find method

ExampleMethodHandles.Lookup lookup = MethodHandles.lookup();MethodType mt = MethodType.methodType(BigDecimal.class, int.class)MethodHandle power = lookup.findVirtual(BigDecimal.class, "pow", mt);

How is a method handle invoked?

Invoked using one of the MethodHandle invoke methodsinvokeExact types of arguments must exactly match the parameter types

invoke transformations will be performed on arguments prior to invocation attempt (e.g. boxing, unboxing, widening)

Method Handle Invocation Examples

// Obtain a lookup context MethodHandles.Lookup lookup = MethodHandles.lookup();// Construct the method typeMethodType mt = MethodType.methodType(BigDecimal.class, int.class);// Obtain the method handleMethodHandle power = lookup.findVirtual(BigDecimal.class, "pow", mt);// Invoke the methodBigDecimal p = (BigDecimal)power.invoke(new BigDecimal(5), 2);p = (BigDecimal)power.invoke(new BigDecimal(5), (byte)2);p = (BigDecimal)power.invoke(new BigDecimal(5), new Integer(2));p = (BigDecimal)power.invokeExact(new BigDecimal(5), 2);// These don't work because arguments or return type don't match//p = (BigDecimal)power.invokeExact(new BigDecimal(5), (byte)2);//p = (BigDecimal)power.invokeExact(new BigDecimal(5), new Integer(2));//Object o = power.invokeExact(new BigDecimal(5), 2);

How are method handles related to reflection?

Convert from Method instance to MethodHandle instance using MethodHandles.Lookup.unreflect method

Method handles more efficient because access at lookup time rather than at invocation time

Method handle API requires pretty specific knowledge of signature to obtain a method handle, unlike reflection

At the end of the day, method handles are the future of introspection

Method Handle API Functional-Flavored Features

MethodHandles class provides methods that can construct method handles from other method handles

ExamplesdropArguments remove arguments

filterArguments pre-process arguments, filter for each

filterReturnValue post-process return value

foldArguments pre-process arguments, apply combiner

guardWithTest applies test to determine invocation

insertArguments binds argument values

permuteArguments reorders arguments

Example insertArgument

MethodHandle insertArguments(MethodHandle target, int pos, Object... values)MethodHandle square = MethodHandles.insertArguments(power, 1, 2);System.out.println(square.invoke(new BigDecimal(5)));

Like currying,where parameters are bound resulting in a new method

What is invokedynamic support?

Introduced in Java 7 to support dynamic method invocation

Primarily for dynamic language designers that create Java bytecode, not Java programmers

Currently not used by Java compiler, but will be in Java 8 with lambda expressions

Consists of a new invokedynamic bytecode and the means for determining at runtime what functionality should be executed when a method is invoked

How is a method invoked using invokdynamic?

invokedynamic bytecode instruction writing to class file when method is dynamic invoked

Argument to invokedynamic instruction is a reference to a bootstrap method that has been written to a special place in the class file

Bootstrap method creates a CallSite instance that can be used at runtime to execute appropriate method

What is a CallSite?

Where method handles come into play with invokedynamic support

CallSite instance created by the bootstrap method that is associated with the invokedynamic instruction

CallSite instance holds a method handle reference (the target) which is invoked by the JVM at runtime

At runtime when the JVM encounters an invokedynamic instruction, it invokes the MethodHandle target associated with the CallSite instance

Runtime dynamic method invocation

What is a lambda expression?

Introduced in Java 8 JSR 335

Represents a block of functionality that can be executed and referenced

Anonymous method

Similar to a closure in other languages, but only allows reference to effectively final (variables declared final or not reassigned) from surrounding scope

Treated by the runtime as an instance of a special type of interface called a functional interface

How is a lambda expression defined?

Syntax is: ->

Parameters are optionally typed

If type omitted it will be inferred by the compiler from the declaration context

Parameter list must be surrounded by parentheses if more than one parameter or explicitly typed

Body is a single expression or a block of statements

Single expression doesn't require braces or an explicit return

Return required if braces used for body

Lambda Expression Examples

() -> System.out.println(Hello!)n -> n > 5(int n) -> n > 5(n) -> {return n > 5;}(a,b) -> Integer.compare(a,b)(a,b) -> {return Integer.compare(a,b);}

What is the type of a lambda expression?

Lamba expressions are like regular Java expressions and have a type

Their type is inferred from the surrounding context by the compiler, called the target type

Lambda expressions can only appear in context where target type is a functional interface

So lambda expressions are represented at runtime by functional interfaces

What is a functional interface?

Interface with a single abstract method

Runnable and Comparator are examples

Package java.util.function contains over 40 functional interfaces

Examples from this package include:Consumer - accepts single input argument and returns no result

Predicate - accepts single input argument and returns boolean result

Lambda expressions and target type

Lambda expressions are implemented using the functional interface target type to infer parameter types and the return type of the lambda expression

This is why lambda expressions don't require explicitly typed parameters or a return type, they can be inferred from target type by the compiler

Target Type Examples

IntPredicate p = n -> n > 5;p.test(4);

Comparator c =(a,b) -> Integer.compare(a,b);c.compare(1,2);

Runnable r = () -> System.out.println(Hello!)r.run();

How are lambda expressions implemented?

invokedynamic is used to allow flexibility of lambda expression implementation

Allows selection of translation strategy to be deferred until runtime

When compiler encounters lambda expression, lambda is 'desugared' into a private static method whose parameter types and return type match that of the functional target type

This method is called by the instance of the functional target type that is generated by the invokedynamic instruction

'Desugaring' Example

IntPredicate p = n -> n > 5;IntPredicate has one method, test that takes an int argument and returns a booleanCompiler creates private static method within the class that contains the lambda expressionprivate static boolean lambda$0(int n) { return n > 5;}Method parameters and return type determined from IntPredicate.test method parameters and return type

The lambda invokedyanmic instruction

The compiler generates an invokedynamic instruction at the point in the code where the lambda expression appears

When invoked, the instruction returns an instance of the functional interface

Dynamic arguments to invokedynamic instruction (those passed to bootstrap method) are values captured from lexical scope

The lambda bootstrap method

Bootstrap method associated with the generated invokedynamic instruction is the method LambdaMethodFactory.metafactory

Takes information about target type and the lambda implementation (desugared) method and generates a CallSite instance that is linked to the invokedynamic instruction

The generated CallSite instance

CallSite generated by the bootstrap method has an associated MethodHandle target that when invoked returns an instance of the functional interface target type associated with the lambda expression

The single method of the returned instance calls the 'desugared' generated method

To summarize at compile-time...

Compiler encounters lambda expression

Compiler determines functional interface target type from context

Compiler generates desugared lambda method

Compiler indicates bootstrap method and provides static arguments about lambda known at compile-time (target type, method handle for desugared lambda, etc.)

Compiler generates invokedynamic instruction referencing bootstrap method

Some javap output

import java.util.function.*;

public class Lambda1 { public static void main(String[] args) { IntPredicate p = n -> n > 5; }}

How are lambda expressions used?

Often used in conjunction with collections and iterators

Also used with event listeners for callbacks

ExamplesList list = Arrays.asList(1,5,4,3,7);

Collections.sort(list, (a,b) -> Integer.compare(a,b));

list.forEach(n -> System.out.println(n));

A Few Additional Java 8 Features

Method referencesAllows a method to be used like a lambda expression

Lambdas define the function body, method references refer to an existing method by name

Kind of like shorthand for a lambda expression that just invokes the named method with the lambda arguments

Kinds of method referencesStatic method (className::methodName)

Instance method (instanceRef::methodName)

Constructor (className::new)

Method Reference Examples

// static method referenceFunction hexMe = Integer::toHexString;hexMe.apply(10);

// instance method referenceString s = "Test it";Predicate m = s::startsWith;m.test("T");

A Few Additional Java 8 Features (cont.)

Default interface methodsAllow interfaces to evolve

Concrete behavior that is added to an interface

Methods of interface are either default or abstract

Default methods have an implementation that is inherited by implementing classes

Don't count towards 'one abstract method' rule of functional interfaces

Can be overridden

Default Method Examples

IterableforEach(Consumer