[JAVA] [JDK 10] Type inference using var

This article is the 16th day of Java Advent Calendar 2017.

Let's consider the contents and usage of JEP 286: Local-Variable Type Inference that will be added in JDK 10.

(Added on 2018.03.22) About operation check with JDK10 official version

The official version of JDK10 was released on March 20, 2018, so I checked if there were any changes since I wrote this article.

C:\Users\chooyan>"C:\Program Files\java\jdk-10\bin\javac.exe" -version
javac 10

As a result, as far as I checked the operation and looked at the following page, it seemed that there was no change in the contents described in this article.

Java 10 Local Variable Type Inference | Oracle Developers

As an aside, the above article also mentions the behavior when you write var list = new ArrayList <> ();, which was talked about in the comment section of this article.

In conclusion, it is treated as ʻArrayList ` and there is no particular merit, so avoid it.

(Addition so far)

Audience of this article

This article

  • I'm working with Java
  • The contents of JEP 286 have not been checked yet.
  • I have little experience with languages other than Java

This article is intended for those who say.

Since var is a keyword that often appears in languages other than Java, some people may expect to compare it, but I don't have enough knowledge and experience to write about it, so this article Then, the content is like introducing the newly introduced var focusing only on Java.

I've written it so that it's the best article for those who want to get a rough idea of Java's var, so I hope you'll read ahead based on that.

JDK to use

This article uses JDK 10 Early-Access Builds.

By downloading and installing the JDK and using javac and jshell in the bin folder, you can try the source code using var like the sample source in this article.

C:\Users\chooyan>"C:\Program Files\java\jdk-10\bin\javac.exe" -version
javac 10-ea

Overview of var

What is being considered in JEP 286: Local-Variable Type Inference is to use var to specify the type when declaring a variable. Let's simplify the code for.

Before we dive into this, let's take a look at the sample code. In this article, we will use jshell for sample code that can be completed in a few lines.

The following is an example of declaring an int type variable.

jshell


jshell> int limit = 20; //Declare an int type variable in the traditional way
limit ==> 20

jshell> var limit = 20; //Declare a variable of type int using var
limit ==> 20

The type declaration for ʻinthas been replaced withvar`.

The same is true for object types. Let's take a look at some.

jshell


jshell> String name = "chooyan_eng"; //Declare a String type variable in the traditional way
name ==> "chooyan_eng"

jshell> var name = "chooyan_eng"; //Declare a variable of type String using var
name ==> "chooyan_eng"

jshell> List<String> idList = new ArrayList<>(); //List in the traditional way<String>Declare a variable of type
idList ==> []

jshell> var idList = new ArrayList<String>(); //List using var<String>Declare a variable of type
idList ==> []

Also, if the right side is a method, you can use var because the return type is clear from that signature.

jshell


jshell> private int sum(int x, int y) {
   ...>     return x + y;
   ...> }

|Created the following:Method sum(int,int)

jshell> var s = sum(4, 5);

s ==> 9

What do you think. As mentioned above, Java's new type inference considered in JEP286 is to reduce the amount of typing and make the code easier to read by replacing the type specification on the left side with var.

var limit

Well, I wrote that you can simplify the type specification on the left side by using the new var, but it is not always possible to use var.

In order to use var, the following conditions must be met.

  • A local variable declared within the method
  • The type is clear when you look at the right side

As a premise, Java's var is " Since the type of the left side can be determined at compile time by looking at the right side, the type specification of the left side can be omitted at the time of coding ". It is not a mechanism that "determines the type at runtime".

To explain in a little more detail, for example, if you create an instance of the ʻArticle class and assign it to the ʻarticle variable, until now

jshell


jshell> Article article = new Article();

article ==> Article@59494225

I wrote. If you look closely, as long as the instance created on the right side is of type ʻArticle, the type of the variable for assigning it must also be of type ʻArticle (or its parent class). And it's still up to the compiler to make an error if it doesn't.

Then, if you write only var on the left side and allow the compiler to infer the actual variable type, it is convenient because you do not have to write the same class twice in one line. Is the idea of Java's var.

Conversely, var cannot be used in situations where the type is not clearly determined by looking at the right side at compile time. From here, I will explain in order what kind of case "the type is not clearly determined even if you look at the right side".

When the type cannot be determined just by looking at the right side

There are some cases where the type cannot be determined just by looking at the right side.

The right side is null

I think it's not uncommon to assign null (or nothing) when declaring a variable.

For example


private Article getArticle(int id) {
    Article article = null;

    if (isOnline()) {
        article = requestArticle(id);
    } else {
        article = selectArticleFromCache(id);
    }

    //Something to do with the article variable

    return article;
}

If you want to change the method of assigning in the if statement, but you want to access the variable outside the if statement, declare the ʻarticle` variable before the if statement and initialize it with null. need to do it.

But at this time

Article article = null;

Variable declaration

var article = null;

Cannot be written. This is because the compiler cannot determine what type is assigned to the variable ʻarticle` because the right-hand side is null with just this one line.

When I actually try it with jshell,

jshell


jshell> var article = null;

|error:
|  cannot infer type for local variable article
|    (variable initializer is 'null')
|  var article = null;
|  ^--------------^

And so, I get an error saying that type inference is not possible with null.

Cannot be used with lambda expression

Next, as a situation where var cannot be used, there is a case where the type of the right side is omitted by the lambda expression.

For example, when creating an anonymous class that inherits the Runnable class as shown below, in the current version of Java, you can write a lambda expression as follows.

jshell


jshell> Runnable runner = () -> { System.out.println("run!"); };

runner ==> $Lambda$15/1471868639@343f4d3d

On the other hand, when I try to write this using the new var, it looks like this:

jshell


jshell> var runner = () -> { System.out.println("run!"); };

|error:
|  cannot infer type for local variable runner
|    (lambda expression needs an explicit target-type)
|  var runner = () -> { System.out.println("run!"); };
|  ^--------------------------------------------^

Since there is no explicit type specification in the lambda expression on the right side, there is a message that var on the left side cannot determine the type. In other words, you can't use lambda expression abbreviation of type and var at the same time.

By the way, in such a case, you can avoid the error by explicitly converting the type as follows.

jshell


jshell> var runner = (Runnable) (() -> {System.out.println("run!");})
runner ==> $Lambda$15/87765719@5442a311

However, in this case, it is easier to read the code by specifying the type on the left side without using var, so you do not need to write it like this.

jshell


jshell> Runnable runner = () -> {System.out.println("run!");};
runner ==> $Lambda$15/87765719@5442a311

Cannot be used with instance variables

As mentioned earlier, var can only be used for" local variables ". In other words, if you try to use it for an instance variable as shown below, an error will occur.

jshell


jshell> class Article {
   ...>     var id = 0;
   ...>     var title = "";
   ...> }

|error:
|  'var' is not allowed here
|      var id = 0;
|      ^-^
|error:
|  'var' is not allowed here
|      var title = "";
|      ^-^

The error message remains the same. It is said that "it cannot be used here".

I don't know the details, but does it mean that only local variables are restricted because it becomes difficult to judge when the scope becomes wide? This area has not been investigated (sorry), but it seems to be such a thing.

Cannot be used to initialize an array without a type declaration

You cannot use var when creating an array without the type declaration, as shown below.

jshell


jshell> var arr = {1, 2, 3}

|error:
|  cannot infer type for local variable arr
|    (array initializer needs an explicit target-type)
|  var arr = {1, 2, 3};
|  ^------------------^

On the other hand, if you specify the type with new int [], there is no problem.

jshell



jshell> var arr = new int[]{1, 2, 3}
arr ==> int[3] { 1, 2, 3 }

It is less likely to use var as the type amount, but here it is better to declare with var after specifying the type like the latter, in order to be consistent with other declaration methods. I felt like.

FAQ with the introduction of var

In addition, I would like to think about what seems to be questionable with the introduction of var in a question-and-answer format.

Q1. What if I want to make it final?

For example, in the case of Kotlin, variables that can be reassigned are var, and variables that cannot be reassigned are val.

In Java, there is currently no such thing as a "non-reassignable var ". Just put final in front of the mold as before.

VarSample.java


public class VarSample {
    public static void main(String[] args) {
        final var id = 2;
        id = 3;
        System.out.println(id);
    }
}

If you compile the above code, you will see that you can no longer assign to variables with final.

$ javac VarSample.java

VarSample.java:4:error:You cannot assign a value to the final variable id
        id = 3;
        ^

Then the question is whether to write the constant as public static final var, but this has the limitation that var can only be used for local variables, so the constant remains the same as before.


public static final String DEFAULT_ID = "9999";

It is described as.

The only modifier that can be attached to a local variable is final, so you probably won't think about the trade-offs with other modifiers.

Q2. I want to receive it on the interface

var receives the right-hand side with the type inferred from the right-hand side.

In other words, it is not possible to receive the generated concrete class in the interface, which was written like a cliché until now.

jshell


//Generated ArrayList<String>An instance of List, which is an interface<String>Receive at
jshell> List<String> idList = new ArrayList<>();  

jshell


//You cannot specify the type to receive when using var
jshell> var idList = new ArrayList<String>();  

If you get into the habit of "handle objects as much as possible with an interface!", You might think this is a very difficult change for a moment, but in reality, this is rarely inconvenient.

Certainly, if it is received by a concrete class, inconvenience will occur when assigning another class with the same interface as shown below.

jshell


jshell> var idList = new ArrayList<String>();
idList ==> []

jshell> idList = new LinkedList<String>();

|error:
|Incompatible type: java.util.LinkedList<java.lang.String>Java.util.ArrayList<java.lang.String>Cannot be converted to:
|  idList = new LinkedList<String>();
|           ^----------------------^

However, you should rarely write code like this in the first place.

In the first place, it is handled in the interface so that any concrete class can be treated in the same way as a method argument or return value (the concrete class can be switched if necessary).

And such usage is still possible with var.

jshell


jshell> private List<String> getIdList() {
   ...>     if (isArrayListSuitable()) {
   ...>         return new ArrayList<String>();
   ...>     } else if (isLinkedListSuitable()) {
   ...>         return new LinkedList<String>();
   ...>     }
   ...>     return new ArrayList<String>();
   ...> }
|Created the following:Method getIdList()

jshell> var idList = getIdList();
idList ==> []

After all, even though var is automatically received by a concrete class, it has a limited impact unless it is available only when declaring a local variable.

Q3. I have already created a class named var.

This is a problem. The problem is that I created a class name called "var" that deviates from the Java naming convention.

In fact, even in JEP286, one of the risks of introducing var is the compatibility problem when the" var "class is already defined.

Risk: source incompatibilities (someone may have used var as a type name.)

However, in the first place, it is unlikely that you will use a word that starts with a lowercase letter as a class name, or even the word "var" that is often used as a keyword in other languages, so "var" should be a new reserved word. It is written that does not change.

Mitigated with reserved type names; names like var do not conform to the naming conventions for types, and therefore are unlikely to be used as types. The name var is commonly used as an identifier; we continue to allow this.

If you've already created a class called var, you'll need to rename it before migrating to JDK 10.

There is no particular problem with the variable name "var". It's a little hard to read, but it can be used in conjunction with the keyword var.

jshell


jshell> int var = 1; //Define int type variable var
var ==> 1

jshell> var = 2; //Assign another value to the variable var
var ==> 2

jshell> var var = 1; //Define variable var using var (int type is inferred)
var ==> 1

Summary

As mentioned above, I checked the var that will be introduced in JDK 10 using the currently released early access version of JDK 10.

For those who are doing languages such as JavaScript, when you hear var, you may get the impression that Java becomes a language whose type changes dynamically, but Java's var is "left side". I think that it was understood that the behavior does not change in particular because it is something like "you can omit the type specification of."

When var becomes available, it is expected that different coding standards and customs will come out, so I think that it can be accepted smoothly by imagining it from now on. think. Please drop the early access version of JDK10 and try it.

In addition, I have not yet read and understood the contents described in JEP 286, so there may be some errors in the contents. not. If you have any strange points, please comment.


Promotion

The other day, I wrote an article I made a repository "Code Your Ruby" for people who want to work in Ruby.

__ "I'm a Java engineer now, but I want to work on Ruby from now on" __ A repository called Code Your Ruby that is ideal for studying We have introduced it, so if you think "it's yourself!", Please read it.

Recommended Posts