Sunday, January 19, 2014

Adapters with Lambda

Java 8 lambdas make some things more interesting:

import org.slf4j.Logger;
import static java.lang.String.format;

interface EasyLogger {
    void log(Throwable cause, String message,
             Object... arguments);

    enum LogLevel {
        TRACE(logger -> logger::trace),
        DEBUG(logger -> logger::debug),
        INFO(logger -> logger::info),
        WARN(logger -> logger::warn),
        ERROR(logger -> logger::error);

        Adapt adapt;

        LogLevel(Adapt adapt) {
            this.adapt = adapt;

        EasyLogger using(Logger logger) {
            return (Throwable cause, String message,
                    Object... arguments)
                -> adapt.from(logger)
                    .log(format(message, arguments), cause);

        static String format(String message, Object... arguments) {
            return null == message
                    ? null : String.format(message, arguments);

        interface Call {
            void log(String message, Throwable cause);

        interface Adapt {
            Call from(Logger logger);

This is just an example, not a real logger class! (Also please excuse the formatting, done to keep the code readable on narrow screens.)

Key point: Java has no "perfect forwarding", so simulate it with method references. Using enums complicated things; essentially these are "factory factories" mapping a level and a logger to a method reference.

Perhaps over clever.

UPDATE: Still a toy class but this makes the interface more familiar:

    default void log(String message, Object... arguments) {
        log(null, message, arguments);

    default void log(Throwable cause) {
        log(cause, null);

An example will help:

import static EasyLogger.INFO;
// Other suitable imports

class Example {
    public static void main(String... args) {
        EasyLogger info = INFO.using(LoggerFactory.getLogger("main"));

        info.log("Hi, %s!", "mom"); 

No comments: