Tuesday 22 July 2014

Java: arbitrary functional interfaces and checked exceptions in Java 8

The last couple of posts have covered how to handle exceptions with Java 8 functional interfaces. This post discusses how to handle arbitrary functional interfaces as of KλudJe 0.2.

Java 8 Method References can be Functional Interface Type Adapters

Java 8 method references provide a limited mechanism to create type adapters.

A trivial application that adapts a Java 8 type to a Google Guava type:

public class Adaptation {
  public static void main(String[] args) {
    java.util.function.Supplier<?> java = () -> "Hello, World!";
    com.google.common.base.Supplier<?> guava = java::get;
    System.out.println(guava.get());
  }
}

Exploiting Adapters with KλudJe

Here is an AutoCloseable instance adapted to a Closeable subtype:

import java.io.Closeable;
import java.io.IOException;
import static uk.kludje.fn.lang.URunnable.asURunnable;

public class AdapterInTheMiddle {
  public static void main(String[] args) throws IOException {
    AutoCloseable original = () -> {
      throw new IOException("expected!");
    };
    Closeable adapter = asURunnable(original::close)::run;
    adapter.close();
  }
}

AutoCloseable generally throws the overly broad Exception type. The URunnable type acts as an intermediary even though it is neither an AutoCloseable nor a Closeable.

Just FYI, here is what the stack trace looks like:

Exception in thread "main" java.io.IOException: expected!
 at AdapterInTheMiddle.lambda$main$0(AdapterInTheMiddle.java:8)
 at AdapterInTheMiddle$$Lambda$1/1160460865.close(Unknown Source)
 at AdapterInTheMiddle$$Lambda$2/2001049719.$run(Unknown Source)
 at uk.kludje.fn.lang.URunnable.run(URunnable.java:18)
 at AdapterInTheMiddle$$Lambda$3/1746572565.close(Unknown Source)
 at AdapterInTheMiddle.main(AdapterInTheMiddle.java:11)

N-ary Interfaces

The Java 8 standard API provides 0, 1 & 2 arity functional interfaces with both void and generic return types. These are: Runnable; Consumer; BiConsumer; Supplier; Function; BiFunction.

KλudJe 0.2 fills the gap in the 3-7 argument range just to provide base types for the exception-propagating versions. See the uk.kludje.fn.nary package for details. If you have more than seven arguments you probably need a copy of Code Complete.

Generating Exception Adapting Types

KλudJe does not provide mirrors for every functional interface or method reference signature imaginable. The n-ary interfaces provided may incur autoboxing costs for interfaces that use primitives.

The exception handling types in KλudJe are generated using annotation processors. Despite being standardized in Java 6 the annotation processor API has seen limited adoption and you may have to jump through hoops to enable processors in your development environment. Caveat emptor.

To generate your own equivalent types you need to depend on the kludje-annotation module. In Maven projects:

<dependency>
    <groupId>uk.kludje</groupId>
    <artifactId>kludje-annotation</artifactId>
    <version>0.2</version>
</dependency>

Create a package for your functional types with a package-info.java file specifying the types you want to process.

//package-info.java for foo
@uk.kludje.annotation.UncheckedFunctionalInterface(foo.YourFuncIface1.class)
@uk.kludje.annotation.UncheckedFunctionalInterface(foo.YourFuncIface2.class)
package foo;

Your build environment will need to execute the uk.kludje.annotation.processor.UncheckedFunctionalInterfaceProcessor annotation processor if it doesn't do it automatically.

No comments:

Post a Comment

All comments are moderated