One handy feature that made the cut in JDK 10 release train is Local-Variable Type Inference (JEP-286). This language feature is part of project Amber, a successor to Project Coin π
With more frequent releases planned for the Java platform, we could expect to see 2 feature releases this year, so March release will be 18.3 and September LTS release will be 18.9. No kidding, Java 10 build#37 shows the release date π
We can expect to see small improvements every six months, so developers don’t have to wait for more than 2 years to catch up with the language. One such productivity-oriented language feature coming in Java 10 is Local-Variable Type Inference (LVTI).
Intellij IDEA 2017.3 has experimental support for this feature.
To enable this feature in your IntelliJ Java project, set the language level to “X – Experimental Features” under Project Settings.
You can trigger this feature from their inspection too, which requires you to accept to their legal.
You can see it automatically detect code that can benefit from this syntactic sugar.
It also supports reverting to old behavior, all the good stuff you are used to with IntelliJ IDEA.
LTVI introduces a new context-sensitive keyword “var”, which technically is not a keyword, instead it is a restricted local variable type and cannot be used for type declarations. So, existing code which uses “var” in variable declarations or as a method name or package name will still be allowed, unlike class or interface name.
Here are some basic examples.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.io.BufferedReader; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.nio.file.Files; | |
import java.nio.file.Paths; | |
import java.util.*; | |
public class LocalVariableTypeInferenceTest { | |
private void processOrder(String order) { | |
System.out.println(order); | |
} | |
public static void main(String[] args) throws Exception { | |
// local variable use | |
var test = new LocalVariableTypeInferenceTest(); | |
// infers HashSet<String> vs Set<String> orders = new HashSet<>(); | |
var orders = new HashSet<String>(); | |
// indexed for-loop use | |
for (var i = 0; i < 5; i++) { | |
orders.add(UUID.randomUUID().toString()); | |
} | |
// for-each use | |
for (var order : orders) { | |
// process an order | |
test.processOrder(order); | |
} | |
// infers Stream<String> | |
var stream = orders.stream(); | |
stream.forEachOrdered(test::processOrder); | |
// try-with-resources use | |
try (var in = Files.newInputStream(Paths.get("./src/LocalVariableTypeInferenceTest.java")); var reader = new BufferedReader(new InputStreamReader(in))) { | |
// print the imports | |
reader.lines().takeWhile(e -> !e.startsWith("public")).forEachOrdered(System.out::println); | |
} catch (IOException e) { | |
throw e; | |
} | |
} | |
} |
Here is another example involving lambda.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Comparator; | |
import java.util.concurrent.CompletableFuture; | |
import java.util.stream.Stream; | |
public class RideTest { | |
interface RideProvider { | |
long getFareEstimate(String start_lat, String start_lng, String end_lat, String end_lng, String type); | |
} | |
public static void main(String[] args) { | |
var start_lat = "37.7749295";//San Francisco | |
var start_lng = "-122.41941550000001";//San Francisco | |
var end_lat = "37.3382082";//San Jose | |
var end_lng = "-121.88632860000001";//San Jose | |
var lyft = new RideProvider() { | |
@Override | |
public long getFareEstimate(String start_lat, String start_lng, String end_lat, String end_lng, String type) { | |
var estimated_cost_cents_max = 0; | |
switch (type) { | |
case "Line": | |
estimated_cost_cents_max = 3307; | |
break; | |
case "Lyft": | |
estimated_cost_cents_max = 7306; | |
break; | |
case "Plus": | |
estimated_cost_cents_max = 12451; | |
break; | |
case "Premier": | |
estimated_cost_cents_max = 17562; | |
break; | |
case "Lux": | |
estimated_cost_cents_max = 23624; | |
break; | |
case "Lux SUV": | |
estimated_cost_cents_max = 25879; | |
break; | |
} | |
return estimated_cost_cents_max; | |
} | |
}; | |
var lyftFuture = CompletableFuture.supplyAsync(() -> lyft.getFareEstimate(start_lat, start_lng, end_lat, end_lng, "Lyft")); | |
var uber = new RideProvider() { | |
@Override | |
public long getFareEstimate(String start_lat, String start_lng, String end_lat, String end_lng, String type) { | |
var estimated_cost_cents_max = 0; | |
switch (type) { | |
case "POOL": | |
estimated_cost_cents_max = 7000; | |
break; | |
case "EXPRESS POOL": | |
estimated_cost_cents_max = 7000; | |
break; | |
case "uberX": | |
estimated_cost_cents_max = 9200; | |
break; | |
case "WAV": | |
estimated_cost_cents_max = 9200; | |
break; | |
case "ASSIST": | |
estimated_cost_cents_max = 9200; | |
break; | |
case "uberXL": | |
estimated_cost_cents_max = 14800; | |
break; | |
case "SELECT": | |
estimated_cost_cents_max = 20900; | |
break; | |
case "SUV": | |
estimated_cost_cents_max = 30600; | |
break; | |
} | |
return estimated_cost_cents_max; | |
} | |
}; | |
var uberFuture = CompletableFuture.supplyAsync(() -> uber.getFareEstimate(start_lat, start_lng, end_lat, end_lng, "uberX")); | |
// compare the futures for best fare estimate | |
Stream.of(lyftFuture, uberFuture) | |
.map(CompletableFuture::join) | |
.min(Comparator.comparing(i -> i)) | |
.ifPresent(estimated_cost_cents_max -> System.out.println("$" + estimated_cost_cents_max / 100)); | |
} | |
} |
If you look at this example carefully, you’ll see our IntelliJ Lambda inspection kicks in for our anonymous inner class.
I replaced it with lambda, but it did not keep IntelliJ happy (of course, this is experimental!).
This one turned out to be a bug in inspection and it has been since fixed.
Support for Lambda Parameters is coming. In fact, there is an experimental support for this feature in lvti branch in amber repository. Maurizio explains the details in this post.
Happy learning folks!
One thought on “Local Variable Type Inference in Java 10”