Friday, January 31, 2014

JDK8: Improved Iterator

One of my favorite changes in Java 8, default methods on interfaces, adds this gem to venerable Iterator:

    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

By default now when you implement an iterable you need not supply "remove()". Beautiful.

There is also a new, defaulted "forEachRemaining(Consumer)".

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"); 
    }
}

Thursday, January 09, 2014

Java 8 AutoCloseable trick

I am working with an API some of whose interfaces have a "close()" method but which do not implement AutoCloseable (they predate this JDK addition), yet I would like to use them in a try-resources block. What to do?

Previously I would give up and just do the try/finally dance — Java has no perfect forwarding, implementing a wrapper class preserving the original's calls is terribly hard.

Java 8 provides a neat trick with method references:

interface Foo {
    void close();

    void doFoo();
}

Elsewhere:

final Foo foo = new SomeFoo();
try (final AutoCloseable ignored = foo::close) {
    foo.doFoo();
}

Presto!

A practical use is pre-4 Spring Framework. When a context type in version 4 declares "close", it implements AutoCloseable; version 3 and earlier do not.

Another neat trick here, the method names need not be the same:

interface Bar {
    void die();

    void doBar();
}

And:

final Bar bar = new SomeBar();
try (final AutoCloseable ignored = bar::die) {
    bar.doBar();
}

Tuesday, January 07, 2014

Links at Google Plus

I've mostly switched over to using my Google Plus account for programming links. If you'd like me to share them with you, please drop me a post. My account there is: Brian Oxley. Longer posts and source code will remain here.