Java 8 Friday: API Designers, be Careful

At Data Geekery, we love Java. And as we’re really into jOOQ’s fluent API and query DSL, we’re absolutely thrilled about what Java 8 will bring to our ecosystem.

Java 8 Friday

Every Friday, we’re showing you a couple of nice new tutorial-style Java 8 features, which take advantage of lambda expressions, extension methods, and other great stuff. You’ll find the source code on GitHub.

Lean Functional API Design

With Java 8, API design has gotten a whole lot more interesting, but also a bit harder. As a successful API designer, it will no longer suffice to think about all sorts of object-oriented aspects of your API, you will now also need to consider functional aspects of it. In other words, instead of simply providing methods like:

void performAction(Parameter parameter);

// Call the above:
object.performAction(new Parameter(...));

… you should now think about whether your method arguments are better modelled as functions for lazy evaluation:

// Keep the existing method for convenience
// and for backwards compatibility
void performAction(Parameter parameter);

// Overload the existing method with the new
// functional one:
void performAction(Supplier<Parameter> parameter);

// Call the above:
object.performAction(() -> new Parameter(...));

This is great. Your API can be Java-8 ready even before you’re actually targeting Java 8. But if you’re going this way, there are a couple of things to consider.

JDK dependency

The above example makes use of the JDK 8 Supplier type. This type is not available before the JDK 8, so if you’re using it, you’re going to limit your APIs use to the JDK 8. If you want to continue supporting older Java versions, you’ll have to roll your own supplier, or maybe use Callable, which has been available since Java 5:

// Overload the existing method with the new
// functional one:
void performAction(Callable<Parameter> parameter);

// Call the above:
object.performAction(() -> new Parameter(...));

One advantage of using Callable is the fact that your lambda expressions (or “classic” Callable implementations, or nested / inner classes) are allowed to throw checked exceptions. We’ve blogged about another possibility to circumvent this limitation, here.

Overloading

While it is (probably) perfectly fine to overload these two methods

void performAction(Parameter parameter);
void performAction(Supplier<Parameter> parameter);

… you should stay wary when overloading “more similar” methods, like these ones:

void performAction(Supplier<Parameter> parameter);
void performAction(Callable<Parameter> parameter);

If you produce the above API, your API’s client code will not be able to make use of lambda expressions, as there is no way of disambiguating a lambda that is a Supplier from a lambda that is a Callable. We’ve also mentioned this in a previous blog post.

“void-compatible” vs “value-compatible”

I’ve recently (re-)discovered this interesting early JDK 8 compiler bug, where the compiler wasn’t able to disambiguate the following:

void run(Consumer<Integer> consumer);
void run(Function<Integer, Integer> function);

// Remember, the above types are roughly:
interface Consumer<T> {
    void accept(T t);
//  ^^^^ void-compatible
}

interface Function<T, R> {
    R apply(T t);
//  ^ value-compatible
}

The terms “void-compatible” and “value-compatible” are defined in the JLS §15.27.2 for lambda expressions. According to the JLS, the following two calls are not ambiguous:

// Only run(Consumer) is applicable
run(i -> {});

// Only run(Function) is applicable
run(i -> 1);

In other words, it is safe to overload a method to take two “similar” argument types, such as Consumer and Function, as lambda expressions used to express method arguments will not be ambiguous.

This is quite useful, because having an optional return value is very elegant when you’re using lambda expressions. Consider the upcoming jOOQ 3.4 transaction API, which is roughly summarised as such:


// This uses a "void-compatible" lambda
ctx.transaction(c -> {
    DSL.using(c).insertInto(...).execute();
    DSL.using(c).update(...).execute();
});

// This uses a "value-compatible" lambda
Integer result =
ctx.transaction(c -> {
    DSL.using(c).update(...).execute();
    DSL.using(c).delete(...).execute();

    return 42;
});

In the above example, the first call resolves to TransactionalRunnable whereas the second call resolves to TransactionalCallable whose API are like these:

interface TransactionalRunnable {
    void run(Configuration c) throws Exception;
}

interface TransactionalCallable<T> {
    T run(Configuration c) throws Exception;
}

Note, though, that as of JDK 1.8.0_05 and Eclipse Kepler (with the Java 8 support patch), this ambiguity resolution does not yet work because of these bugs:

So, in order to stay on the safe side, maybe you could just simply avoid overloading.

Generic methods are not SAMs

Do note that “SAM” interfaces that contain a single abstract generic method are NOT SAMs in the sense for them to be eligible as lambda expression targets. The following type will never form any lambda expression:

interface NotASAM {
    <T> void run(T t);
}

This is specified in the JLS §15.27.3

A lambda expression is congruent with a function type if all of the following are true:

  • The function type has no type parameters.
  • [ … ]

What do you have to do now?

If you’re an API designer, you should now start writing unit tests / integration tests also in Java 8. Why? For the simple reason that if you don’t you’ll get your API wrong in subtle ways for those users that are actually using it with Java 8. These things are extremely subtle. Getting them right takes a bit of practice and a lot of regression tests. Do you think you’d like to overload a method? Be sure you don’t break client API that is calling the original method with a lambda.

That’s it for today. Stay tuned for more awesome Java 8 content on this blog.

Top 10 Ceylon Language Features I Wish We Had In Java

What does one do when Hibernate is “finished” and feature complete and one needs new challenges? Right. One creates a new JVM language called Ceylon.

On November 12, 2013, Ceylon 1.0.0 was finally released and we congratulate the whole team at Red Hat for their achievements in what looks like a very promising new JVM language. While it will be a slight challenge for Ceylon to compete with Scala, there are lots of very interesting features that distinguish it.

In fact, this language has so many interesting features, it’ll be hard to write up a blog post about the 10 most interesting ones. Which ones to choose? On Google Plus, I’ve had a short chat with Gavin King who also brought us Hibernate, Ross Tate who is also involved with JetBrains’ Kotlin, and Lukas Rytz who was a PhD student and committer for EPFL’s Scala and now works at Google Dart. I wanted those language Uberdesigners to help me find the 10 most thrilling language features that they have and we Java developers don’t. Now I have 20 interesting ones. I’ll certainly write a follow-up post to this one.

I have observed Gavin King and the other guys to be very enthusiastic and knowledgeable. I’ve already had this impression before when I first heard about Ceylon from Stéphane Épardaud at the JUGS in Berne, Switzerland in February 2013, another one of RedHat’s passionate engineers (see his presentation’s slides here).

Anyway, enough of the who’s who. Here’s our personal Top 10 List of Ceylon Language Features I Wish We Had In Java:

1. Modules

In Java, Jigsaw has been postponed about 34 times and we’re only now closing in on Java 8 GA! Yes, we have OSGi and Maven, and both work very well to manage dependencies at runtime (OSGi) or at compile-time (Maven). But compare this black magic Maven/OSGi configuration using Apache Felix

<plugin>
  <groupId>org.apache.felix</groupId>
  <artifactId>maven-bundle-plugin</artifactId>
  <version>2.1.0</version>
  <extensions>true</extensions>
  <executions>
    <execution>
      <id>bundle-manifest</id>
      <phase>process-classes</phase>
      <goals>
        <goal>manifest</goal>
      </goals>
    </execution>
  </executions>
  <configuration>
    <supportedProjectTypes>
      <supportedProjectType>
        jar
      </supportedProjectType>
    </supportedProjectTypes>
    <instructions>
      <Bundle-SymbolicName>
        org.jooq
      </Bundle-SymbolicName>
      <Export-Package>*</Export-Package>
      <Import-Package>
        javax.persistence;resolution:=optional,
        org.apache.log4j;resolution:=optional,
        *
      </Import-Package>
      <_versionpolicy>
        [$(version;==;$(@)),$(version;+;$(@)))
      </_versionpolicy>
    </instructions>
  </configuration>
</plugin>

… with this one by Ceylon:

"The second best ever ORM solution!"
license "http://www.gnu.org/licenses/lgpl.html"
module org.hibernate "3.0.0.beta" {
    import ceylon.collection "1.0.0";
    import java.base "7";
    shared import java.jdbc "7";
}

Finally, things can be controlled on a jar-level, including visibility of packages. With only few lines of code. Please, Java, integrate Ceylon’s powerful module support.

It may be worth mentioning that Fantom is another language with integrated module support. See JodaTime’s Stephen Colebourne’s talk at Devoxx 2011: “Is Fantom Light Years Ahead of Scala?”. Stephen has also brought us ElSql, a new external SQL DSL for Java templating.

2. Sequences

This is the first time I’ve seen this kind of first class support for sequences in a typesafe language. Not only does Ceylon ship with all sorts of collection literals, it also knows types for these constructs. Concretely, you can declare an Iterable as such:

{String+} words = { "hello", "world" };

Notice the notation of the literal. It is of type {String+}, meaning that it contains at least one element. The type is assignment-compatible with {String*}, which represents a possibly empty sequence. Very interesting.

This goes on by supporting array literals as such:

String[] operators = [ "+", "-", "*", "/" ];
String? plus = operators[0];
String[] multiplicative = operators[2..3];

… or tuple literals:

[Float,Float,String] point = [0.0, 0.0, "origin"];

Notice also the range literal 2..3 which allows for extracting sub-arrays from the original array. So much sequence goodness in Ceylon!

Notice also the question mark in String?, which is Ceylon’s way of declaring …

3. Nullable types

While Scala knows the Option type and Haskell knows the Maybe type and Java 8 tries to compete by adding the new, unenforceable Optional type, Ceylon has a very simple notion of something that is nullable. If there’s a question mark behind a type, it’s nullable. Otherwise, it’s not null. Always.

In order to convert a nullable type into a not nullable type, you have to explicitly check:

void hello() {
    String? name = process.arguments.first;
    String greeting;
    if (exists name) {
        greeting = "Hello, ``name``!";
    }
    else {
        greeting = "Hello, World!";
    }
    print(greeting);
}

Notice the exists operator. It defines a new scope within which the name variable is known to be not null, i.e. it is promoted from String? to String. This locally scoped type promotion is commonly referred to as flow-sensitive typing, which has already been observed in the Whiley language, according to Lukas Rytz.

If you omit the exists check, you’d get a compilation error on that string interpolation there. There are also other useful constructs to perform ad-hoc type conversions:

String greeting = "Hello, " + (name else "World");

The else clause acts like a SQL COALESCE() function and can even be chained. Read more about Ceylon’s nullable goodness.

4. Defaulted parameters

OMG, how I wish we had that in Java. Every time we overload methods, we think, why not just support defaulted parameters like PL/SQL, for instance??

void hello(String name="World") {
    print("Hello, ``name``!");
}

I cannot think of a single good reason why languages wouldn’t have named and defaultable parameters like PL/SQL:

-- One of the parameters is optional
CREATE PROCEDURE MY_PROCEDURE (
  P1 IN NUMBER,
  P2 IN VARCHAR2 := 'ABC',
  P3 IN VARCHAR2
);

-- Calling the procedure
MY_PROCEDURE(
  P1 => 1,
  P3 => 'XYZ'
);

So this is one way to circumvent method overloading in most common cases. Method overloading is still tedious when we want to deal with alternative, incompatible types. But not in Ceylon, as Ceylon knows …

5. Union types

OK, this is a bit esoteric. The creators of Ceylon really really wanted to get rid of method overloading, partially because Ceylon also compiles to JavaScript, and JavaScript does not know function overloading. In fact, it is not possible to overload methods in Ceylon at all. To be able to interoperate with Java, however, union types needed to be introduced. A union type String|Integer can be either a String or an Integer. There’s method overloading right there!

void printType(String|Integer|Float val) { ... }
 
printType("hello");
printType(69);
printType(-1.0);

In order to “untangle” the union type, you can again take advantage of flow-sensitive typing for the val parameter by performing type-checks similar to Java’s instanceof

void printType(String|Integer|Float val) {
    switch (val)
    case (is String) { print("String: ``val``"); }
    case (is Integer) { print("Integer: ``val``"); }
    case (is Float) { print("Float: ``val``"); }
}

Within that scope, val is known to the compiler to be of type String, for example. This goes on to allowing crazy stuff like enumerated types where a type can be one or another thing, simultaneously:

abstract class Point()
        of Polar | Cartesian {
    // ...
}

Note that this is very different from multiple inheritance where such a Point would be both Polar and Cartesian. But that’s not all. Ceylon also has …

6. Intersection types

Now, as you may have guessed, that’s the exact inverse of a union type, and this is actually also supported by Java’s generics. In Java, you can write:

class X<E extends Serializable & Comparable<E>> {}

In the above example, X accepts only type parameters that are both Serializable and Comparable. This is much crazier in Ceylon where you can assign values to a locally declared intersection type. And that’s not it! In our chat, Gavin has pointed out this incredible language feature to me, where union / intersection types can interact with flow-sensitive typing to form the following (due for Ceylon 1.2):

value x = X();
//x has type X
if (something) {
    x = Y();
    //x has type Y
}
//x has type X|Y

Makes sense, right? So I asked him, if I will be able to intersect that type again with Z and Gavin said, yes! The following can be done:

value x = X();
//x has type X
if (something) {
    x = Y();
    //x has type Y
}
//x has type X|Y
if (is Z x) {
    //x has type <X|Y>&Z
}

And this goes on, because type intersections also interact with generics in a very interesting way. Under certain circumstances, X<A>&X<B> can be the same as X<A&B>. In other words, intersections (and unions) are distributive with generics, just like additions are with multiplications (in an informal understanding of “just like”). If you’re willing to delve into the language spec for this, see §3.7.2 Principal instantiation inheritance.

Now, union and intersection types can get quite nasty und hard to reuse. This is why Ceylon has …

7. Type aliases

Is there any other programming language that ever thought of this awesome feature?? This is so useful, even if you’re not supporting union and/or intersection types. Think about Java’s generics. With the advent of generics, people started writing stuff like:

Map<String, List<Map<Integer, String>>> map = // ...

Two things can be said:

  • Generics are extremely useful to the Java libraries
  • Generics become extremely verbose when doing the above

Here’s where type aliases come into play. Check out this example:

interface People => Set<Person>;

The point here is that even if some verbose types are reused very often, you don’t often want to create an explicit subtype for the above. In other words, you don’t want to abuse subtype polymorphism as a shortcut to “simplify” generic polymorphism.

Think of aliases as an expandable macro, which is mutually assignment-compatible. In other words, you can write:

People?      p1 = null;
Set<Person>? p2 = p1;
People?      p3 = p2;

So as the term “alias” suggests, you’re not creating a new type. You’re just giving a complex type a simpler name. But even better than type aliasing is …

8. Type inference

Many other languages have this and so does Java to a certain extent, at least as far as generics are involved. Java 8 goes one step further in allowing type inference with generics. But Java is far away from what languages like Scala or Ceylon can do with local variables:

interface Foo {}
interface Bar {}
object foobar satisfies Foo&Bar {}
//inferred type Basic&Foo&Bar
value fb = foobar; 
//inferred type {Basic&Foo&Bar+}
value fbs = { foobar, foobar };

So, this example shows a lot of features combined, including type constraints, sequence types, union types. With such a rich type system it is very important to support this level of type inference where a value keyword indicates that you don’t want to (or you cannot) explicitly declare a type. This, I’d really love to see in Java 9!

Read more about Ceylon’s awesome type inference capabilities.

9. Declaration-site variance

Now, this feature might be a bit harder to understand, as Java’s generics are already quite difficult to understand. I’ve recently read a very interesting paper by Ross Tate, Alan Leung and Sorin Lerner about the challenges brought to Java generics through wildcards: Taming Wildcards in Java’s Type System. Generics are still a very active research topic neither researchers nor language designers completely agree on whether use-site variance (as in Java) or declaration-site variance (as in C#, Scala, or Ceylon) is really better for mainstream programmers. Older languages talking about variance are Eiffel and OCaml.

Microsoft has introduced declaration-site variance in C#. I’ll cite the example from Wikipedia, which is very easy to understand. In C#, the IEnumerator interface has a covariant generic type parameter:

interface IEnumerator<out T>
{
    T Current { get; }
    bool MoveNext();
}

This simply means that the following will work:

IEnumerator<Cat> cats = ...
IEnumerator<Animal> animals = cats;

This is quite different from Java’s use-site variance, where the above wouldn’t compile, but the following would:

Iterator<Cat> cats = ...
Iterator<? extends Animal> animals = cats;

The main reason for declaration-site covariance is the simple fact that verbosity is greatly reduced at the use-site. Wildcards are a major pain to Java developers and they lead to numerous Stack Overflow questions as this one, which is about locally scoped wild-cards:

// Given this interface:
public interface X<E> {
    E get();
    E set(E e);
}

// This does not compile:
public void foo(X<?> x) {
    x.set(x.get());
}

As can be seen in the Ceylon language tour, Ceylon generics support declaration-site variance, just like C# and Scala. It will be interesting to see how these things evolve, as both types of variance support have their pros and cons, while at the same time, Ross Tate advocates mixed-site variance, which would really be a great addition for the Java language!

Now this was a bit complex, so let’s have a look at a simpler, yet awesome feature to round things up …

10. Functions and methods

One of the main things outlined by Stéphane Épardaud was the fact that the Ceylon language is a very regular language. This is particularly apparent when considering how Ceylon treats functions (and methods, which are type member functions). I can put a function everywhere. Consider this example:

Integer f1() => 1;
class C() {
    shared Integer f2() {
        Integer f3() => 2;
        return f3();
    }
}

print(f1());
print(C().f2());

In the above example,

  • f1() is a package-level function (much like a “global” static function in Java)
  • f2() is a regular method on the C class
  • f3() is a local function within the f2() method

With Java 8’s support for lambda expressions, these things get a bit better, but isn’t it awesome to be able to declare functions anywhere, in almost the same syntax?

Conclusion: Play around with Ceylon

That’s it for now. We might be publishing a follow-up article about the more esoteric language features in Ceylon, some time soon. In any case, you can download this interesting JVM language for free with first-class IDE support in Eclipse. You can also visit the Ceylon documentation website and have their website compile Ceylon code into JavaScript for execution in your browser.

Visit the Community and interact with the language designers from RedHat and Serli, and when you’re done, share this post on our jOOQ blog and help the JCP recognise that this wonderful language has a couple of very interesting features to put on the Java 9 or 10 roadmap!

Java Auto-Unboxing Gotcha. Beware!

What do you think that the following code snippet will print?

Object o = true ? new Integer(1) : new Double(2.0);
System.out.println(o);

Yes! It will print:

1.0

What? 1.0? But I have assigned an Integer to my o variable. Why does it print 1.0? It turns out that there is a subtle little specification section in the JLS’s §15.25, which specifies the ternary operator. Here’s what is applied to the above:

The type of a conditional expression is determined as follows:

  • […]
  • Otherwise, if the second and third operands have types that are convertible (§5.1.8) to numeric types, then there are several cases:
    • […]
    • Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.

      Note that binary numeric promotion performs value set conversion (§5.1.13) and may perform unboxing conversion (§5.1.8).

Binary numeric promotion may implicitly perform unboxing conversion! Eek! Who would have expected this? You can get a NullPointerException from auto-unboxing, if one of the operands is null, the following will fail

Integer i = new Integer(1);
if (i.equals(1))
    i = null;
Double d = new Double(2.0);
Object o = true ? i : d; // NullPointerException!
System.out.println(o);

Obviously (obviously !?) you can circumvent this problem by casting numeric types to non-numeric types, e.g. Object

Object o1 = true 
  ? (Object) new Integer(1) 
  : new Double(2.0);
System.out.println(o1);

The above will now print

1

Credits for discovery of this gotcha go to Paul Miner, who has explained this more in detail here on reddit.

Overload API methods with care

Overloading methods is a strong concept in API design, especially when your API is a fluent API or DSL (Domain Specific Language). This is the case for jOOQ, where you often want to use the exact same method name for various means of interaction with the library.

Example: jOOQ Conditions

package org.jooq;

public interface Condition {

    // Various overloaded forms of the "AND" operation:
    Condition and(Condition other);
    Condition and(String sql);
    Condition and(String sql, Object... bindings);

    // [...]
}

All of these methods connect two conditions with each other using an “AND” operator. Ideally, the implementations depend on each other, creating a single point of failure. This keeps things DRY:

package org.jooq.impl;

abstract class AbstractCondition implements Condition {

    // The single point of failure
    @Override
    public final Condition and(Condition other) {
        return new CombinedCondition(
            Operator.AND, Arrays.asList(this, other));
    }

    // "Convenience methods" delegating to the other one
    @Override
    public final Condition and(String sql) {
        return and(condition(sql));
    }

    @Override
    public final Condition and(String sql, Object... bindings) {
        return and(condition(sql, bindings));
    }
}

The trouble with generics and overloading

When developing with Eclipse, the Java 5 world seems more shiny than it really is. Varargs and generics were introduced as syntactic sugar in Java 5. They don’t really exist in that way in the JVM. That means, the compiler has to link method invocations correctly, inferring types if needed, and creating synthetic methods in some cases. According to the JLS (Java Language Specification), there is a lot of ambiguity when varargs/generics are employed in overloaded methods.

Let’s elaborate on generics:

A nice thing to do in jOOQ is to treat constant values the same as fields. In many places, field arguments are overloaded like this:

// This is a convenience method:
public static <T> Field<T> myFunction(Field<T> field, T value) {
    return myFunction(field, val(value));
}

// It's equivalent to this one.
public static <T> Field<T> myFunction(Field<T> field, Field<T> value) {
    return MyFunction<T>(field, value);
}

The above works very well in most of the cases. You can use the above API like this:

Field<Integer> field1  = //...
Field<String>  field2  = //...

Field<Integer> result1 = myFunction(field1, 1);
Field<String>  result2 = myFunction(field2, "abc");

But the trouble arises when <T> is bound to Object!

// While this works...
Field<Object>  field3  = //...
Field<Object>  result3 = myFunction(field3, new Object());

// ... this doesn't!
Field<Object>  field4  = //...
Field<Object>  result4 = myFunction(field4, field4);
Field<Object>  result4 = myFunction(field4, (Field) field4);
Field<Object>  result4 = myFunction(field4, (Field<Object>) field4);

When <T> is bound to Object, all of a sudden both methods apply, and according to the JLS, none of them is more specific! While the Eclipse compiler is usually a bit more lenient (and in this case intuitively links the second method), the javac compiler doesn’t know what to do with this call. And there is no way around it. You cannot cast field4 to Field or to Field<Object> to force the linker to link to the second method. That’s pretty bad news for an API designer.

For more details about this special case, consider the following Stack Overflow question, which I reported as a bug to both Oracle and Eclipse. Let’s see which compiler implementation is correct:

http://stackoverflow.com/questions/5361513/reference-is-ambiguous-with-generics

The trouble with static imports, varargs

There is more trouble for API designers, that I will document some other time.