Three new JEPs

Three new JEPs

This morning Mark Reinhold submitted three brand new JEPs (JDK Enhancement Proposal).

  • JEP 300: Augment Use-Site Variance with Declaration-Site Defaults
  • JEP 301: Enhanced Enums
  • JEP 302: Lambda Leftovers

These proposals are enhancements to the JDK (Java Development Kit) and OpenJDK. A long term roadmap for the JDK projects, a look into what the future of Java might hold.

Let’s dive right in and take a quick look on what these proposals actually are!

JEP 300: Augment Use-Site Variance with Declaration-Site Defaults

When you currently use Java Generics you probably already know about wildcards. It is possible to set lower and upper bounds to generics using the keywords ? extends and ? super. There are two parts to using wildcards, declaration side and use side, this JEP focusses mainly on making the use side easier and more powerful.

Declaration side

It is possible to set a bound with a wildcard on the declaration side:

public class Shelter<X extends Animal> {
	...
}

In this example we’ve declared that the Shelter has to contain Animals. It is possible to create an Shelter<Cat> or Shelter<Dog> now, but not Shelter<Bike>.

Use side

The second way to use wildcards is on the use side. There we can speak of so called in and out-variable wildcards.

The following code uses the ‘extends’ keyword to create the in-variable:

    List<Animal> animals = new ArrayList<>();

    // Using extends we create an 'in' variable:
    List<? extends Animal> inAnimals = animals;

    // From the inAnimals we can read: Animal (not subclasses)
    Animal animal = inAnimals.get(0); // <- reading Animal is fine
    Cat cat = inAnimals.get(0); // <- compile error, we can't read Cat

    // We can write: nothing (!) making it almost read-only
    inAnimals.add(new Animal()); // <- compile error

There is also the ‘super’ keyword (not very common) to create an out-variable use side:

    List<Animal> animals = new ArrayList<>();

    // Using super we create an 'out' variable, setting an upper bound:
    List<? super Cat> outAnimals = animals;

    // From the outAnimals we can read: object (no superclasses!)
    Cat cat = outAnimals.get(0); // <- compile error, illegal
    Object o = outAnimals.get(0); // <- we can only read object

    // On the write side, we've set a bound to Cat:
    outAnimals.add(new Cat()); // <- correct!
    outAnimals.add(new Object()); // <- compile error
    outAnimals.add(new Animal()); // <- compile error

So what is JEP 300?

The motivation of the JEP states:

Since invariant uses of these type arguments are less flexible than their wildcard equivalents, while providing no extra power, a reasonable practice is to always use a wildcard when mentioning the type.

It is almost always more powerful and equivalent to use wildcards on the use side. The problem is that this is verbose and adds a lot of noise to your code. The proposal wants to make it possible to declare (at the declaration side) what the default wildcard strategy should be.

For example look at the following code:

    interface Function<contravariant T, covariant R> {
        R apply(T arg);
    
    }
    // other possible syntaxes are Function<-T, +R> or Function<in T, out R>

The compiler can now automatically treat every use of the type e.g., Function<String, Number> as if it had wildcards Function<? super String, ? extends Number>.

On the use side the proposal says:

Rather than making changes to subtyping, we preprocess the source code so that types like Function<String, Number> are implicitly expanded to Function<? super String, ? extends Number>.

This should make it more powerful and just as readable to using wildcards by default without even noticing them.

JEP 301: Enhanced Enums

The next proposal Mark has submitted is about using Enums with Generics. Look at the following example:

enum Pet {

    BELLA(new Dog("Bella")),
    BUDDY(new Dog("Buddy")),
    TIGGER(new Cat("Tigger"));

    Animal animal;

    Pet(Animal animal) {
        this.animal = animal;
    }

    public Animal get() {
        return animal;
    }
}

We have an enum with three pets. These pets have an instance of an animal inside them. And we have a way of retrieving them.

System.out.println( Pet.BELLA.get().getName() ); // prints: "Bella"

But one thing we can’t do it call the method “.bark()” on BELLA, because we don’t know this is a Dog.

The proposal JEP 301 wants to make it possible to correlate a specific type to an enum constant:

enum Pet<A extends Animal> {

    BELLA<Dog>(new Dog("Bella")),
    BUDDY<Dog>(new Dog("Buddy")),
    TIGGER<Cat>(new Cat("Tigger"));

    A animal;

    Pet(A animal) {
        this.animal = animal;
    }

    public A get() {
        return animal;
    }
}

Having coupled the Dog type to enum BELLA it should now we possible to call:

Pet.BELLA.get().bark(); // WOOF!
Pet.TIGGER.get().purr(); // Purrrrrrrrr.....

JEP 302: Lambda Leftovers

This is turning out to be a rather long blogpost but we’ve reached the final proposed JEP of the day: Lambda Leftovers

Accidental ambiguities

One of the problems people are having with the lambdas at the moment, and this JEP wants to improve, are accidental ambiguities.

For example:

private void apply(Predicate<Animal> filter) {
   ...
}

private void apply(Function<Animal, String> function) {
   ...
}

apply(input -> false); // <- compile error

/**
 * Ambiguous method call. Both:
 * apply (Predicate<Animal>)        in classname and
 * apply (Function<Animal, String>) in classname match
 */
    

Why are we having this problem? Both methods are possible candidates of the lambda. If we look further and see the return type ‘boolean’ it should be clear we wanted to call the Predicate method and not the Function (which should return a String). Right now this doesn’t work, but it is something the compiler could figure out for us.

Underscores

Up to Java 8 it was perfectly acceptable to use the underscore character ‘_’ as a variable name. But this has been changed, all leading up to JEP 302. In most languages with lambda’s it is possible to denote certain inputs as unused. This is done using the underscore. In Java this causes problems because it is right now a valid variable name.

Starting with Java 8 the use of an underscore as lambda argument caused a warning and from Java 9 the use of an underscore became an error. This allows future Java versions beyond 9 to be able to use the underscore for other purposes. And one of those purposes is to bring it back as a default variable name. Not causing collisions if used multiple times and it can’t be used as variable by calling _.toString() for example.

// Ignore the second parameter, just use i:
BiFunction<Integer, String, String> biss = (i, _) -> String.valueOf(i);

// Even possible, ignore both:
BiFunction<Integer, String, String> b2 = (_, _) -> "someString";
    

Shadowing of parameters

Another small change that is proposed is allowing to shadow parameters. For example look at the following code:

Map<String, Integer> map = ...
String key = computeSomeKey();

map.computeIfAbsent(key, key -> key.length()) // compile error, key already defined

map.computeIfAbsent(key, _ -> {
    String key = "";  // compile error, can't redefine key inside this scope
    return key.length();
});

In both cases ‘key’ is already defined, can’t be used as a parameter or local variable inside the lambda. The proposal wants to lift this restriction, allowing the use of ‘key’ as a parameter or as a local inside the lambda using so called ‘shadowing’.

One possible drawback is readability, and I too think that this might be a problem. For example:

Map<String, Integer> map = ...
String key = "theInitialKey";

map.computeIfAbsent(key, _ -> {
    String key = "theShadowKey"; // <- delete this
    return key.length();
});

The second variable “theShadowKey” is taking the place of the first “theInitialKey”. This can be quite confusing because if you delete the second declaration nothing would break. The other variable would come from the shadows and take its place. This feels dangerous and confusing to me, not very ‘Java’-like.

Conclusion

Another day, three new JEPs submitted. Java is moving forward and trying to improve the user experience with genetics and lambdas. These proposals look like good, small, low-impact improvements that can really benefit the common programmer.