Java 8: too little, too late?

Lately, I attended an event that Oracle organized in Athens, which also served as the inaugural event for Java 8. I consider Java 8 a major release (unlike prior “major” releases) which does merit discussion. And, as I usually play the Devil’s Advocate, I’m going to share some thoughts on it.

I’ll focus on certain new features: the most prominent are Streams and lambdas, but Optional and default methods are also worth discussing. I won’t discuss new time support, or any other library improvement.

Streams is an attempt to give Java a real collection library; only, the library consists of a single interface, the Stream, and you have to convert back and forth between it and the real data structures. Still, it’s exciting to go from almost zero to being able to do map, filter, flatMap, fold, reduce etc in Java.

Lambdas will help a lot to bring some long-needed conciseness to Java. But they’re not really lambda expressions, meaning, anonymous functions. The only place where they can exist is where a Functional interface is expected (and a functional interface, in short, is any interface representing a single concern – more on that later). Combined with some limited type inference, this does give a lot of leverage without doing real changes to the language. If you were planning to do heavy higher-order functional stuff in Java, don’t hold high hopes, because the type signatures you have to write will likely dissuade you. There’s no type inference to ease the burden. And no predefined function interface for three or more arguments. But it’s nice for collection operations and such.

Since you know that I don’t have time for lengthy posts, here is a Java 8 sample which demonstrates, in a single shot, various things about Java 8.

    public static void main(String[] args) {
        final Optional<Integer> integerOptional =
                Arrays.stream(args)
                .findFirst()
                .flatMap(a -> getEl(getTheMap(), a))
                .flatMap(SampleUseOptional::tryParseInt)
                .map(i -> i*2);
    }

    public static <K,V> Optional<V> getEl(Map<K,V> map, K key) {
        return map.containsKey(key) ? Optional.of(map.get(key)) : Optional.empty();
    }

    public static Optional<Integer> tryParseInt(String s) {
        try {
            return Optional.of(Integer.valueOf(s));
        } catch (NumberFormatException e) {
            return Optional.empty();
        }
    }

    private static HashMap<String, String> getTheMap() { /* ... */ }

The meat, of course, is the monadic workflow in main. But notice how artfully I crammed in there

  1. the fact that, even arrays can be made into streams, even though they were second-class collections up to Java 8
  2. the fact that, using defender (default) methods to enhance existing interfaces can do nothing for Java arrays, hence the need for Arrays.stream
  3. the fact that Optional is also a monad in Java 8, as are collections (by virtue of Streams)
  4. the fact that Optional is not integrated where it makes sense, like Map.get or Integer.parse
  5. the fact that there’s shorthand for referring to a method
  6. the fact that there’s no shorthand to refer to a curried method (e.g. getEl(getTheMap())

Let’s work on that lack of currying for a moment. Surely there’s some (verbose) way to define what currying means. Here is an appropriate definition for a two-argument, may I dare say?, function.

    private static <T1,T2,R> Function<T2,R> curryFirstOfTwo(BiFunction<T1,T2,R> f, T1 x) {
        return y -> f.apply(x,y);
    }

Ok, that was not so bad. This is still Java, after all. Let’s try to use it.

        final Optional<Integer> integerOptional3 =
                Arrays.stream(args)
                .findFirst()
                .flatMap(curryFirstOfTwo(SampleUseOptional::getEl, getTheMap()))
                .flatMap(SampleUseOptional::tryParseInt)
                .map(i -> i * 2);

Hmm, IDEA chokes on this, and stops complaining only when the first argument to curryFirstOfTwo is rewritten as a lambda with typed arguments, but the actual compiler fares better. So this amount of boilerplate gives you an idea of what you can expect when you want to go a little further than the design space of the new features. Still, it’s feasible. And don’t forget all the fun, implementing interfaces for functions of arity N > 2!

I didn’t cover what Optional is, because it is what its name implies: it’s what Scala and F# call Option, Haskell calls Maybe and Guava already had with the same name. It’s your way out of NPE Hell, and how you’ll avoid being ridiculed by your C/C++ programmer friends to which you dared boast one day that programming in Java is safer. They’re still laughing, but not for long. The simplicity of this idea makes it be misunderstood. How can anything so simple have so much value? Aren’t nullity declarations, non-nullable types, nullity inference, elvis operators and such (found in Kotlin, Gosu, Fantom), similar, if not better? No, in my opinion, they aren’t. But this is a discussion for another day. The important thing is that Optional is now part of Java 8, and yet it isn’t, since the whole behemoth of its library is still exception-waiting-to-happen-ridden. But it can be part of your codebase.

Default methods, which were added to allow Oracle to extend interfaces, but now allow anyone to do as much, bring multiple (implementation) inheritance in Java. It’s that simple. No diamond problem, according to a presentation from Oracle I watched. At least, no diamond problem if you restrict the problem to how state is initialized in multiple inheritance, not how the rest of the world defines it, in a more general sense. With great power comes great responsibility, and Java, breaking out of its cocoon, starts having grown-up-language problems. Welcome, Java!

So, is Java 8 too little, too late? In my opinion, no and no. It doesn’t come up ahead. It just plays catch with everyone else. But it’s the first time it did anything to approach everyone else in a long time.

Already, Java 8 supersedes Guava for functional-ish programming (my up-to-now escape to sanity while doing Java), which had opted for a more limited set of features. Will it clash with Scala? Certainly not. If anything, it will pull more developers out of the stagnating waters older versions of Java had left them in. Thousands of people had stopped thinking about where programming can go to. Out of some twisted notion of devotion to Java? Out of safety in doing what thousands of others do? Out of arrogance for belonging to the biggest programming community? Something, in any case. And now, it is these people that are the least qualified to make the move to Java 8, while anyone with experience in any other JVM, .Net or any other language should feel right at home.

However, I am pleased to say that many more thousands of people are thirsty for more, which is a sign of sanity in the community. Adoption of Java 8 has a chance of being a lot faster than that of previous releases, and any JVM language will benefit from that happening. Not to mention that, increasing the momentum of the JVM might trigger faster evolution of over-the-horizon features like value classes and real arrays.

Advertisements

Spring break

My witty-title-generator was a bit off for this one. The pun does not imply that Spring is broken, not that it has broken anything. But what I’m going to describe is bound to shake (and break?) a misconception around it.

Consider Spring JavaConfig (now officially part of the Spring distribution). I admit it’s not my favorite configuration option (and – both – regular readers know I have strong opinions on DI). But many people prefer it to XML configuration. And some of them believe something about it, which is not true.

Classes annotated with @Configuration are scanned for bean declarations. At the same time, this annotation also creates a bean for the class. So far, so good. Throw @DependsOn to the picture, which can be used to inform Spring about a dependency which cannot be established through the regular means of constructor or property injection. Don’t get me started about how often people skip proper DI in favor of: getting hold of the context, shamelessly pulling a bean out, then using @DependsOn to force the dependency. I might write another post about this (if I find a way to do it without giving in to my impulse to shout it in all caps and lots of exclamation marks). Suppose, for argument’s sake, that its use is warrantied because of some hidden dependency on a side effect or something.

The problem, which prompted this post, is that some people think that using @DependsOn, from a configuration class to some other configuration class, affects the initialization order of the beans declared in them.

Let me demonstrate what’s wrong with this idea, and show you what @DependsOn really does in that case. Classes foo and bar are configuration classes, but I have also exposed their bean faces, by declaring initialization methods in both of them (imports elided for brevity).

@Configuration
public class foo {
	@PostConstruct
	public void init() {
		System.out.println("foo.init()");
	}

	@Bean
	public Long foobean() {
		System.out.println("foobean");
		return System.currentTimeMillis();
	}
}

@Configuration
@DependsOn("foo")
public class bar {
	@PostConstruct
	public void init() {
		System.out.println("bar.init()");
	}

	@Bean
	public Long barbean() {
		System.out.println("barbean");
		return System.currentTimeMillis();
	}
}

The two unit tests that follow register them in two different orders. Scanning a package is the same, except the registering order is arbitrary and does not lead itself to a repeatable test. I’ll tell you how to conduct that test later, if you want to try it.

	@Test
	public void testDependsOnOrderOne() {
		System.out.println("Running test with bar scanned before foo, and bar having @DependsOn(\"foo\")");
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
		ctx.register(bar.class);
		ctx.register(foo.class);
		ctx.refresh();
		final Long foobean = ctx.getBean("foobean", Long.class);
		final Long barbean = ctx.getBean("barbean", Long.class);
		Assert.assertTrue(barbean < foobean);
	}

	@Test
	public void testDependsOnOrderTwo() {
		System.out.println("Running test with foo scanned before bar, and bar having @DependsOn(\"foo\")");
		AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
		ctx.register(foo.class);
		ctx.register(bar.class);
		ctx.refresh();
		final Long foobean = ctx.getBean("foobean", Long.class);
		final Long barbean = ctx.getBean("barbean", Long.class);
		Assert.assertTrue(barbean > foobean);
	}

Have a look at what these tests (which both succeed) output.

Running test with bar scanned before foo, and bar having @DependsOn("foo")
foo.init()
bar.init()
barbean
foobean
Running test with foo scanned before bar, and bar having @DependsOn("foo")
foo.init()
bar.init()
foobean
barbean

Notice that @DependsOn does have an effect. It has the effect it is designed to have: force an initialization order between foo and bar. Notice also that it does not have the effect it is believed to have: force any order between beans inside foo and bar.

Scanning a package is the exact same thing, only the scanning order is not guaranteed. But it’s easy to overcome this problem if first you establish what order happens to be used in your setup, then fixing the @DependsOn the other way and assert that it is ignored.

If you’ve been using @DependsOn between configuration classes, it is time you did some Spring cleaning (a more appropriate pun than the one in the title?).

Lies, damned lies and methods implementing specialized generic interfaces

You know that I’ve been spoiled by spending years in a land with reified generics. I’ve heard Daniel Spiewak, in an episode of “The Scalawags“, say that he prefers erased generics because they permit more freedom in the typesystem, and Martin Odersky also swears by them, so I’m not ready to condemn them yet. But they can sure tie one in knots.

Here’s a recount of a recent experience of mine, suitably distilled to its essentials. Follows javap disassembly for a completely boring pair of interface and implementation.

Compiled from "Interface.java"
public interface Interface {
  public abstract boolean toBoolean(java.lang.String);
}
Compiled from "Implementation.java"
public class Implementation implements Interface {
  public Implementation();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public boolean toBoolean(java.lang.String);
    Code:
       0: iconst_1
       1: ireturn
}

Surely you must be yawning by now. Let’s spice it up by introducing some Java 8-like base interface. toBoolean looks like Predicate#test, doesn’t it? But this time I’ll also show you the original Java, because you’re in for a surprise.

public interface Predicate<T> {
  public abstract boolean test(T);
}

public interface Interface2 extends Predicate<java.lang.String> {
}

public class Implementation2 implements Interface2 {
 public boolean test(String s) {
  return true;
 }
}
Compiled from "Implementation2.java"
public class Implementation2 implements Interface2 {
  public Implementation2();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public boolean test(java.lang.String);
    Code:
       0: iconst_1
       1: ireturn

  public boolean test(java.lang.Object);
    Code:
       0: aload_0
       1: aload_1
       2: checkcast     #2                  // class java/lang/String
       5: invokevirtual #3                  // Method test:(Ljava/lang/String;)Z
       8: ireturn
}

What the…? Apparently, the mere act of defining Interface2 as a specialization of Predicate, actually specialized the signature of test, so as to allow uses of “real” test(String). But, at the same time, the compiler injected an erased test that calls the specialized one. That’s how I found it. I was creating the implementation with Javassist, and the Javassist compiler did not perform this sleigh-of-hand. Which was OK until I tried to call the method and got an abstract method error. Not a very common sight, is it?

Oh, and you cannot do this explicitly with Java code, because trying to do so gives you an error.

OtherImplementation2b.java:2: error: name clash: test(Object) in OtherImplementation2b and test(T) in Predicate have the same erasure, yet neither overrides the other
 public boolean test(Object o) {
                ^
  where T is a type-variable:
    T extends Object declared in interface Predicate
1 error

boolean test(java.lang.Object) should have been final, but isn’t. Even so, the aforementioned error does not let you override it, anyway. You can only override the specialized method.

Ultimately, it is not just compilers for “other” languages who need to jump into hoops to get around the limitations of Generics. This is a case of the Java compiler itself jumping through hoops. Remember that the Javassist-created class compiled fine and was loaded fine, but the error stroke at runtime. Beware!

UPDATE: These phantom methods are called “bridge” methods, and they are utilized in all places where the typesystem needs to express things the JVM cannot. They sport the ACC_BRIDGE flag.