The Java Local Variable Type Inference (LVTI) or briefly the type var (the var identifier is not a keyword, but a reserved type name) was added to Java 10 using JEP 286: Local-Variable Type Inference . Being a 100% compiler function, it does not affect bytecode, runtime, or performance. Basically, the compiler checks the right side of the assignment operator and, based on it, determines the specific type of the variable, and then replaces it with var .
In addition, it is useful for reducing the verbosity of the template code, as well as allows you to speed up the programming process itself. For example, it is very convenient to write var evenAndOdd =...
instead of Map<Boolean, List<Integer>> evenAndOdd =...
The appearance of var does not mean that it is always convenient to use it everywhere, sometimes it will be more practical to do with standard tools.
In this article we will look at 26 situations, with examples of when var can be used and when it is not worth doing.
Usually we focus on giving the correct names to the fields of the classes, but we do not pay the same attention to the names of local variables. When our methods are perfectly implemented, contain little code and have good names, then very often we do not pay attention to local variables, or even abbreviate their names.
When we use var instead of writing explicit types, the compiler automatically identifies them and substitutes var instead. But on the other hand, as a result, people find it harder to read and understand the code, since using var can complicate its readability and understanding. In most cases, this happens because we tend to look at the type of a variable as the primary information, and its name as the secondary information. Although it should be just the opposite.
Probably many would agree that in the example below the names of local variables are too short:
// HAVING public boolean callDocumentationTask() { DocumentationTool dtl = ToolProvider.getSystemDocumentationTool(); DocumentationTask dtt = dtl.getTask(...); return dtt.call(); }
When using short names in conjunction with var , the code becomes even less clear:
// AVOID public boolean callDocumentationTask() { var dtl = ToolProvider.getSystemDocumentationTool(); var dtt = dtl.getTask(...); return dtt.call(); }
More preferred option:
// PREFER public boolean callDocumentationTask() { var documentationTool = ToolProvider.getSystemDocumentationTool(); var documentationTask = documentationTool.getTask(...); return documentationTask.call(); }
Avoid naming variables like this:
// AVOID public List<Product> fetchProducts(long userId) { var u = userRepository.findById(userId); var p = u.getCart(); return p; }
Use more meaningful names:
// PREFER public List<Product> fetchProducts(long userId) { var user = userRepository.findById(userId); var productList = user.getCart(); return productList; }
In an effort to give clearer names to local variables, do not go to extremes:
// AVOID var byteArrayOutputStream = new ByteArrayOutputStream();
Instead, you can use a shorter, but no less clear option:
// PREFER var outputStream = new ByteArrayOutputStream(); // or var outputStreamOfFoo = new ByteArrayOutputStream();
Did you know that Java has an inner class named:
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState
Well, naming variables with this type can be tricky :)
Without the use of literals for primitive types, we may find that the expected and expected types may differ. This is caused by the implicit type conversion, which is used by var- variables.
For example, the following two code fragments behave as expected. Here we explicitly declare types boolean and char :
boolean flag = true; // this is of type boolean char a = 'a'; // this is of type char
Now use var , instead of explicit type declaration:
var flag = true; // this is inferred as boolean var a = 'a'; // this is inferred as char
So far so good. And now let's do the same for the types int , long , float and double :
int intNumber = 20; // this is of type int long longNumber = 20; // this is of type long float floatNumber = 20; // this is of type float, 20.0 double doubleNumber = 20; // this is of type double, 20.0
Although the code snippet above is simple and straightforward, now let's use var , instead of explicitly specifying types.
Avoid:
// AVOID var intNumber = 20; // this is inferred as int var longNumber = 20; // this is inferred as int var floatNumber = 20; // this is inferred as int var doubleNumber = 20; // this is inferred as int
All four variables will be displayed as int . To fix this behavior, we need to use Java literals:
// PREFER var intNumber = 20; // this is inferred as int var longNumber = 20L; // this is inferred as long var floatNumber = 20F; // this is inferred as float, 20.0 var doubleNumber = 20D; // this is inferred as double, 20.0
But what happens if we declare a number with a decimal part?
Avoid this if you expect to get a float variable:
// AVOID, IF THIS IS A FLOAT var floatNumber = 20.5; // this is inferred as double
To avoid surprise, use the appropriate literal:
// PREFER, IF THIS IS A FLOAT var floatNumber = 20.5F; // this is inferred as float
For example, let's assume that our code is between two methods. One method gets a shopping cart with different products and calculates the best price. To do this, it compares various prices in the market and returns the total price as a float . Another method simply deducts this price from the card.
First, let's look at the method that calculates the best price:
public float computeBestPrice(String[] items) { ... float price = ...; return price; }
Secondly, let's take a look at the method that works with the card:
public boolean debitCard(float amount, ...) { ... }
Now we put our code between these two external service methods as a client. Our users can choose products to buy, and we expect the best price for them, and then write off money from the card:
// AVOID public void purchaseCart(long customerId) { ... float price = computeBestPrice(...); debitCard(price, ...); }
After some time, the company that owns the API decides to abandon the real price representation in favor of the decimal (instead of float , int is now used). So they modified the API code as follows:
public int computeBestPrice(String[] items) { ... float realprice = ...; ... int price = (int) realprice; return price; } public boolean debitCard(int amount, ...) { ... }
The point is that our code uses the explicit declaration of a float variable as the price. In its current form, we will receive an error at compile time. But if we had foreseen such a situation and used var instead of float , then our code would continue to work without problems, thanks to the implicit type conversion:
// PREFER public void purchaseCart(long customerId) { ... var price = computeBestPrice(...); debitCard(price, ...); }
Some primitive types in Java do not have special literals, for example, byte and short types. In this case, using explicit type notation, we can create variables without any problems.
Use this instead of var :
// PREFER THIS INSTEAD OF USING VAR byte byteNumber = 45; // this is of type byte short shortNumber = 4533; // this is of type short
But why in this situation, give preference to the explicit designation of types instead of just using var ? Well, let's write this code using var . Note that in both cases, the compiler will assume that you need variables of type int .
Avoid this error:
// AVOID var byteNumber = 45; // this is inferred as int var shortNumber = 4533; // this is inferred as int
There are no literals that would come to our aid, therefore we are forced to use explicit descending type conversions. Personally, I will avoid such situations, because I do not see any advantages here.
Use this entry only if you really want to use var :
// PREFER THIS ONLY IF YOU WANT TO USE VAR var byteNumber = (byte) 45; // this is inferred as byte var shortNumber = (short) 4533; // this is inferred as short
The advantage of using var is to write more concise code. For example, in the case of constructors, we can avoid having to repeat the class name and, therefore, eliminate the redundancy of the code.
Avoid the following:
// AVOID MemoryCacheImageInputStream inputStream = new MemoryCacheImageInputStream(...);
Instead, use:
// PREFER var inputStream = new MemoryCacheImageInputStream(...);
For the construction below, var will also be a good way to simplify the code without losing any information.
Avoid:
// AVOID JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fm = compiler.getStandardFileManager(...);
Use the following code:
// PREFER var compiler = ToolProvider.getSystemJavaCompiler(); var fileManager = compiler.getStandardFileManager(...);
So why is it more comfortable for us to work with var in the examples presented? Because all the necessary information is contained in the names of variables. But if var , in conjunction with the variable name, leads to a decrease in the clarity of the code, it is better to refuse to use it.
Avoid:
// AVOID public File fetchCartContent() { return new File(...); } // As a human, is hard to infer the "cart" type without // inspecting the fetchCartContent() method var cart = fetchCartContent();
Use:
// PREFER public File fetchCartContent() { return new File(...); } File cart = fetchCartContent();
Consider, for example, using the java.nio.channels.Selector
class. This class has a static open()
method that returns a new selector and opens it. But here you can easily think that the Selector.open()
method can return a boolean type, depending on the success of opening an existing selector, or even return void . Using var here will lead to information loss and confusion in the code.
This means that we cannot compile an application that tries to do a mis-assignment. For example, the code below will not compile:
// IT DOESN'T COMPILE var items = 10; items = "10 items"; // incompatible types: String cannot be converted to int
But this one will compile:
var items = 10; items = 20;
And this code is successfully compiled:
var items = "10"; items = "10 items";
Once the compiler has determined the value of the var variable, we cannot assign anything else except this type.
In Java, we use the "programming using interfaces" approach. For example, we create an instance of the ArrayList class, associating it with an abstraction (interface):
List<String> products = new ArrayList<>();
And we avoid things like binding an object to a variable of the same type:
ArrayList<String> products = new ArrayList<>();
This is the most common and desirable practice, since we can easily replace the interface implementation with any other. For this, it is only necessary to declare an interface type variable.
We will not be able to follow this concept using var variables, since a specific type is always displayed for them. For example, in the following code snippet, the compiler will determine the type of the variable as ArrayList<String>
:
var productList = new ArrayList<String>(); // inferred as ArrayList<String>
There are several var arguments that explain this behavior:
var is used for local variables, where, in most cases, programming using interfaces is used less than in cases with method parameters, return values ββor fields
The scope of local variables should be small, so solving the problems caused by switching to another implementation should not be a big deal.
var interprets the code to the right as an initializer used to determine the actual type. If, at some point, the initializer is changed, then the type being defined may also change, causing problems in code that relies on this variable.
Using var in conjunction with a diamond operator (<>) in the absence of information to identify the type, can lead to unexpected results.
Before Java 7, explicit types were used for collections:
// explicitly specifying generic class's instantiation parameter type List<String> products = new ArrayList<String>();
Since Java 7, the diamond operator has been introduced In this case, the compiler will display the required type:
// inferring generic class's instantiation parameter type List<String> products = new ArrayList<>();
What type will be displayed in the code below?
You should avoid similar constructions:
// AVOID var productList = new ArrayList<>(); // is inferred as ArrayList<Object>
The type will be defined as ArrayList<Object>
. This is because the information needed to correctly determine the type is not presented. This leads to the fact that the nearest type will be selected, which can be compatible with the context of what is happening. In this case, Object
.
Thus, var can only be used if we provide the necessary information to determine the expected type. The type can be specified directly or passed as an argument.
Directly specify the type:
// PREFER var productList = new ArrayList<String>(); // inferred as ArrayList<String>
Pass arguments of the required type:
var productStack = new ArrayDeque<String>(); var productList = new ArrayList<>(productStack); // inferred as ArrayList<String>
Product p1 = new Product(); Product p2 = new Product(); var listOfProduct = List.of(p1, p2); // inferred as List<Product> // DON'T DO THIS var listofProduct = List.of(); // inferred as List<Object> listofProduct.add(p1); listofProduct.add(p2);
We all know how to declare arrays in Java:
int[] numbers = new int[5]; // or, less preferred int numbers[] = new int[5];
How about using var when working with arrays? In this case, there is no need to use brackets on the left side.
Avoid the following (it will not even compile):
// IT DOESN'T COMPILE var[] numbers = new int[5]; // or var numbers[] = new int[5];
Use:
// PREFER var numbers = new int[5]; // inferred as array of int numbers[0] = 2; // work numbers[0] = 2.2; // doesn't work numbers[0] = "2"; // doesn't work
The code below does not compile with var either. This is because the compiler cannot determine the type on the right side:
// explicit type work as expected int[] numbers = {1, 2, 3}; // IT DOESN'T COMPILE var numbers = {1, 2, 3}; var numbers[] = {1, 2, 3}; var[] numbers = {1, 2, 3};
If you like to declare variables of the same type at once, then you need to know that var is not suitable for this. The following code will not compile:
// IT DOESN'T COMPILE // error: 'var' is not allowed in a compound declaration var hello = "hello", bye = "bye", welcome = "welcome";
Instead, use:
// PREFER String hello = "hello", bye = "bye", welcome = "welcome";
Or that:
// PREFER var hello = "hello"; var bye = "bye"; var welcome = "welcome";
Keep a small scope for local variables β I'm sure you heard this statement before the appearance of var .
Readability and quick bug fixes are arguments for this approach. For example, let's define a stack like this:
Avoid this:
// AVOID ... var stack = new Stack<String>(); stack.push("George"); stack.push("Tyllen"); stack.push("Martin"); stack.push("Kelly"); ... // 50 lines of code that doesn't use stack // George, Tyllen, Martin, Kelly stack.forEach(...); ...
Note that we call the forEach()
method, which inherits from java.util.Vector
. This method will go through the stack like any other vector and this is what we need. But now we decided to use ArrayDeque
instead of Stack
. When we do this, the forEach()
method will get an implementation from ArrayDeque, which will run along the stack as a standard stack (LIFO)
// AVOID ... var stack = new ArrayDeque<String>(); stack.push("George"); stack.push("Tyllen"); stack.push("Martin"); stack.push("Kelly"); ... // 50 lines of code that doesn't use stack // Kelly, Martin, Tyllen, George stack.forEach(...); ...
This is not what we want. Itβs too difficult to track down the error, because the code that contains the forEach()
part is not next to the code that has been changed. To increase the speed of searching and correcting errors, it is much better to write code that uses the stack
variable as close as possible to declaring this variable.
Itβs best to do this:
// PREFER ... var stack = new Stack<String>(); stack.push("George"); stack.push("Tyllen"); stack.push("Martin"); stack.push("Kelly"); ... // George, Tyllen, Martin, Kelly stack.forEach(...); ... // 50 lines of code that doesn't use stack
Now, when the developer switches from Stack
to ArrayQueue
, he can quickly notice the error and correct it.
We can use different types of operands on the right side of the ternary operator.
If you explicitly specify types, the following code will not compile:
// IT DOESN'T COMPILE List code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10); // or Set code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10);
Nevertheless, we can do this:
Collection code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10); Object code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10);
The code below also does not compile:
// IT DOESN'T COMPILE int code = intOrString ? 12112 : "12112"; String code = intOrString ? 12112 : "12112";
But you can use more general types:
Serializable code = intOrString ? 12112 : "12112"; Object code = intOrString ? 12112 : "12112";
In all such cases it is better to prefer var :
// PREFER // inferred as Collection<Integer> var code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10); // inferred as Serializable var code = intOrString ? 12112 : "12112";
It does not follow from these examples that the var type determines the types of objects at run time. This is not true!
And, of course, the var type will work correctly with the same types of both operands:
// inferred as float var code = oneOrTwoDigits ? 1211.2f : 1211.25f;
We can easily replace the explicit type declaration in for loops with var .
Changing an explicit int type to a var :
// explicit type for (int i = 0; i < 5; i++) { ... } // using var for (var i = 0; i < 5; i++) { // i is inferred of type int ... }
Changing the explicit Order
type to var :
List<Order> orderList = ...; // explicit type for (Order order : orderList) { ... } // using var for (var order : orderList) { // order type is inferred as Order ... }
It is very simple to use var from Java 10 with streams (stream) that appeared in Java 8.
You can simply replace the explicit Stream declaration with a var :
// explicit type Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5); numbers.filter(t -> t % 2 == 0).forEach(System.out::println); // using var var numbers = Stream.of(1, 2, 3, 4, 5); // inferred as Stream<Integer> numbers.filter(t -> t % 2 == 0).forEach(System.out::println);
// explicit types Stream<String> paths = Files.lines(Path.of("...")); List<File> files = paths.map(p -> new File(p)).collect(toList()); // using var var paths = Files.lines(Path.of("...")); // inferred as Stream<String> var files = paths.map(p -> new File(p)).collect(toList()); // inferred as List<File>
Expressions with great nesting look impressive and usually seem like some clever and important parts of the code. In the case when it is necessary to facilitate the readability of the code, it is recommended to split a large expression using local variables. But sometimes writing a lot of local variables seems like a very exhausting job that I would like to avoid.
An example of a large expression:
List<Integer> intList = List.of(1, 1, 2, 3, 4, 4, 6, 2, 1, 5, 4, 5); // AVOID int result = intList.stream() .collect(Collectors.partitioningBy(i -> i % 2 == 0)) .values() .stream() .max(Comparator.comparing(List::size)) .orElse(Collections.emptyList()) .stream() .mapToInt(Integer::intValue) .sum();
Better break the code into its component parts:
List<Integer> intList = List.of(1, 1, 2, 3, 4, 4, 6, 2, 1, 5, 4, 5); // PREFER Map<Boolean, List<Integer>> evenAndOdd = intList.stream() .collect(Collectors.partitioningBy(i -> i % 2 == 0)); Optional<List<Integer>> evenOrOdd = evenAndOdd.values() .stream() .max(Comparator.comparing(List::size)); int sumEvenOrOdd = evenOrOdd.orElse(Collections.emptyList()) .stream() .mapToInt(Integer::intValue) .sum();
The second version of the code looks more readable and simpler, but the first version also has a right to exist. It is absolutely normal for our mind to adapt to the understanding of such large expressions and prefer them to local variables. However, using the var type can help with breaking up large structures by reducing the effort to declare local variables:
var intList = List.of(1, 1, 2, 3, 4, 4, 6, 2, 1, 5, 4, 5); // PREFER var evenAndOdd = intList.stream() .collect(Collectors.partitioningBy(i -> i % 2 == 0)); var evenOrOdd = evenAndOdd.values() .stream() .max(Comparator.comparing(List::size)); var sumEvenOrOdd = evenOrOdd.orElse(Collections.emptyList()) .stream() .mapToInt(Integer::intValue) .sum();
The two code snippets shown below will not compile.
Using var as the return type:
// IT DOESN'T COMPILE public var countItems(Order order, long timestamp) { ... }
Using var as the type of the method argument:
// IT DOESN'T COMPILE public int countItems(var order, var timestamp) { ... }
The following code snippets will compile and will work properly:
public int countItems(Order order, long timestamp) { ... } public boolean checkOrder() { var order = ...; // an Order instance var timestamp = ...; // a long representing a timestamp var itemsNr = countItems(order, timestamp); // inferred as int type ... }
Ρ Π΄ΠΆΠ΅Π½Π΅ΡΠΈΠΊΠ°ΠΌΠΈ Π²ΡΠ΅ ΡΠ°ΠΊ ΠΆΠ΅ Π±ΡΠ΄Π΅Ρ ΡΠ°Π±ΠΎΡΠ°ΡΡ ΠΎΡΠ»ΠΈΡΠ½ΠΎ:
public <A, B> B contains(A container, B tocontain) { ... } var order = ...; // Order instance var product = ...; // Product instance var resultProduct = contains(order, product); // inferred as Product type
ΠΠΌΠ΅ΡΡΠΎ ΡΠ²Π½ΠΎΠ³ΠΎ ΡΠΊΠ°Π·Π°Π½ΠΈΡ ΡΠΈΠΏΠΎΠ²:
public interface Weighter { int getWeight(Product product); } // AVOID Weighter weighter = new Weighter() { @Override public int getWeight(Product product) { ... } }; Product product = ...; // a Product instance int weight = weighter.getWeight(product);
ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ var :
public interface Weighter { int getWeight(Product product); } // PREFER var weighter = new Weighter() { @Override public int getWeight(Product product) { ... } }; var product = ...; // a Product instance var weight = weighter.getWeight(product);
ΠΡΠΏΠΎΠΌΠ½ΠΈΠΌ, ΡΡΠΎ:
β¦ Π½Π°ΡΠΈΠ½Π°Ρ Ρ Java SE 8, Π»ΠΎΠΊΠ°Π»ΡΠ½ΡΠΉ ΠΊΠ»Π°ΡΡ ΠΌΠΎΠΆΠ΅Ρ ΠΎΠ±ΡΠ°ΡΠ°ΡΡΡΡ ΠΊ Π»ΠΎΠΊΠ°Π»ΡΠ½ΡΠΌ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠΌ ΠΈ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡΠ°ΠΌ Π·Π°ΠΊΠ»ΡΡΠ°ΡΡΠ΅Π³ΠΎ Π±Π»ΠΎΠΊΠ°, ΠΊΠΎΡΠΎΡΡΠ΅ ΡΠ²Π»ΡΡΡΡΡ final ΠΈΠ»ΠΈ effectively final. ΠΠ΅ΡΠ΅ΠΌΠ΅Π½Π½Π°Ρ ΠΈΠ»ΠΈ ΠΏΠ°ΡΠ°ΠΌΠ΅ΡΡ, Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΠΊΠΎΡΠΎΡΡΡ Π½ΠΈΠΊΠΎΠ³Π΄Π° Π½Π΅ ΠΈΠ·ΠΌΠ΅Π½ΡΠ΅ΡΡΡ ΠΏΠΎΡΠ»Π΅ ΠΈΡ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΠΈ, ΡΠ²Π»ΡΡΡΡΡ effectively final .
Π§ΡΠΎ ΠΆ, ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ ΡΠΈΠΏΠ° var ΠΌΠΎΠ³ΡΡ Π±ΡΡΡ effectively final. ΠΡΠΎ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ²ΠΈΠ΄Π΅ΡΡ Π² ΡΠ»Π΅Π΄ΡΡΡΠ΅ΠΌ ΠΏΡΠΈΠΌΠ΅ΡΠ΅.
ΠΠ·Π±Π΅Π³Π°ΠΉΡΠ΅:
public interface Weighter { int getWeight(Product product); } // AVOID int ratio = 5; // this is effectively final Weighter weighter = new Weighter() { @Override public int getWeight(Product product) { return ratio * ...; } }; ratio = 3; // this reassignment will cause error
ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅:
public interface Weighter { int getWeight(Product product); } // PREFER var ratio = 5; // this is effectively final var weighter = new Weighter() { @Override public int getWeight(Product product) { return ratio * ...; } }; ratio = 3; // this reassignment will cause error
ΠΠ·Π½Π°ΡΠ°Π»ΡΠ½ΠΎ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ var ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΠΎΠΉ ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ΠΎ (Π·Π° ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅ΠΌ, ΠΊΠΎΠ³Π΄Π° ΠΎΠ½Π° ΠΎΠ±ΡΡΠ²Π»Π΅Π½Π° ΠΊΠ°ΠΊ effectively final). ΠΠΎ ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ ΠΎΠ±ΡΡΠ²ΠΈΡΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ, ΠΊΠ°ΠΊ final .
ΠΠ·Π±Π΅Π³Π°ΠΉΡΠ΅:
// AVOID // IT DOESN'T COMPILE public void discount(int price) { final int limit = 2000; final int discount = 5; if (price > limit) { discount++; // this reassignment will cause error, which is ok } }
ΠΡΠ΅Π΄ΠΏΠΎΡΠΈΡΠ°ΠΉΡΠ΅:
// PREFER // IT DOESN'T COMPILE public void discount(int price) { final var limit = 2000; final var discount = 5; if (price > limit) { discount++; // this reassignment will cause error, which is ok } }
Π’ΠΈΠΏ var Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡΡΡ, Π΅ΡΠ»ΠΈ Π½Π΅Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ ΠΊΠΎΠ½Π΅ΡΠ½ΡΠ΅ ΡΠΈΠΏΡ. Π’Π°ΠΊΠΈΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΡΠ΅ΡΠ΅Π· Π»ΡΠΌΠ±Π΄Π° Π²ΡΡΠ°ΠΆΠ΅Π½ΠΈΡ ΠΈ ΡΡΡΠ»ΠΊΠΈ Π½Π° ΠΌΠ΅ΡΠΎΠ΄Ρ, Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ var , Π½Π΅ ΡΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡΡΠ΅ΡΡΡ:
// IT DOESN'T COMPILE // lambda expression needs an explicit target-type var f = x -> x + 1; // method reference needs an explicit target-type var exception = IllegalArgumentException::new;
ΠΠΌΠ΅ΡΡΠΎ ΡΡΠΎΠ³ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅:
// PREFER Function<Integer, Integer> f = x -> x + 1; Supplier<IllegalArgumentException> exception = IllegalArgumentException::new;
ΠΠΎ Π² Java 11 ΡΠ°Π·ΡΠ΅ΡΠ΅Π½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ var -ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ Π² ΠΊΠΎΠ½ΡΠ΅ΠΊΡΡΠ΅ Π»ΡΠΌΠ±Π΄Π° Π²ΡΡΠ°ΠΆΠ΅Π½ΠΈΠΉ. Π‘Π»Π΅Π΄ΡΡΡΠΈΠΉ ΠΏΡΠΈΠΌΠ΅Ρ ΠΊΠΎΠ΄Π° Π·Π°ΡΠ°Π±ΠΎΡΠ°Π΅Ρ Π² Java 11:
// Java 11 (var x, var y) -> x + y // or (@Nonnull var x, @Nonnull var y) -> x + y
ΠΠ°ΠΏΡΠ΅ΡΠ΅Π½ΠΎ ΠΎΠ±ΡΡΠ²Π»ΡΡΡ var -ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ Π±Π΅Π· ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΠΈ.
ΠΡΠΎΡ ΠΊΠΎΠ΄ Π½Π΅ ΡΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡΡΠ΅ΡΡΡ (ΠΏΠΎΠΏΡΡΠΊΠ° ΠΏΡΠΈΡΠ²ΠΎΠΈΡΡ null ):
// IT DOESN'T COMPILE var message = null; // result in an error of type: variable initializer is 'null'
Π ΡΡΠΎΡ ΡΠΎΠΆΠ΅ Π½Π΅ ΡΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡΡΠ΅ΡΡΡ (ΠΎΡΡΡΡΡΡΠ²ΡΠ΅Ρ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΎΡ):
// IT DOESN'T COMPILE var message; // result in: cannot use 'var' on variable without initializer ... message = "hello";
Π ΡΡΠΎΡ ΠΊΠΎΠ΄ ΡΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡΡΠ΅ΡΡΡ ΠΈ Π±ΡΠ΄Π΅Ρ ΠΈΡΠΏΡΠ°Π²Π½ΠΎ ΡΠ°Π±ΠΎΡΠ°ΡΡ:
// PREFER String message = null; // or String message; ... message = "hello";
ΠΡ ΠΌΠΎΠΆΠ΅ΡΠ΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ var Π΄Π»Ρ Π»ΠΎΠΊΠ°Π»ΡΠ½ΡΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ , Π½ΠΎ Π½Π΅ Π² ΠΊΠ°ΡΠ΅ΡΡΠ²Π΅ ΠΏΠΎΠ»Π΅ΠΉ ΠΊΠ»Π°ΡΡΠΎΠ².
ΠΡΠΎ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠ΅ ΠΏΡΠΈΠ²Π΅Π΄Π΅Ρ ΠΊ ΠΎΡΠΈΠ±ΠΊΠ°ΠΌ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΈΠΈ:
// IT DOESN'T COMPILE public class Product { private var price; // error: 'var' is not allowed here private var name; // error: 'var' is not allowed here ... }
ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ ΡΠ°ΠΊΠΎΠΉ ΡΠΏΠΎΡΠΎΠ±:
// PREFER public class Product { private int price; private String name; ... }
Π’Π΅ΠΌ Π½Π΅ ΠΌΠ΅Π½Π΅Π΅, ΡΡΠΎ ΡΠ°Π·ΡΠ΅ΡΠ΅Π½ΠΎ Π² try-with-resources
ΠΠΎΠ³Π΄Π° ΠΊΠΎΠ΄ Π±ΡΠΎΡΠ°Π΅Ρ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΠ΅, ΠΌΡ Π΄ΠΎΠ»ΠΆΠ½Ρ ΠΏΠΎΠΉΠΌΠ°ΡΡ Π΅Π³ΠΎ, ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΡ ΠΊΠΎΠ½ΠΊΡΠ΅ΡΠ½ΡΠΉ ΡΠΈΠΏ.
Π‘Π»Π΅Π΄ΡΡΡΠΈΠΉ ΠΊΠΎΠ΄ Π²ΡΠ·ΠΎΠ²Π΅Ρ ΠΎΡΠΈΠ±ΠΊΡ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΈΠΈ:
// IT DOESN'T COMPILE try { TimeUnit.NANOSECONDS.sleep(5000); } catch (var ex) { ... }
Π ΡΠ°ΠΊΠΎΠΌ ΡΠ»ΡΡΠ°Π΅ Π½Π΅ΠΎΠ±Ρ ΠΎΠ΄ΠΈΠΌΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ ΡΠ²Π½ΡΠΉ ΡΠΈΠΏ ΠΈΡΠΊΠ»ΡΡΠ΅Π½ΠΈΡ:
// PREFER try { TimeUnit.NANOSECONDS.sleep(5000); } catch (InterruptedException ex) { ... }
ΠΠ΄Π½Π°ΠΊΠΎ, var ΠΎΡΠ»ΠΈΡΠ½ΠΎ ΡΠ°Π±ΠΎΡΠ°Π΅Ρ Π² Π±Π»ΠΎΠΊΠ΅ try-with-resources .
ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ, ΡΡΠΎΡ ΠΊΠΎΠ΄:
// explicit type try (PrintWriter writer = new PrintWriter(new File("welcome.txt"))) { writer.println("Welcome message"); }
ΠΠΎΠΆΠ½ΠΎ Π·Π°ΠΌΠ΅Π½ΠΈΡΡ ΠΊΠΎΠ΄ΠΎΠΌ Ρ var :
// using var try (var writer = new PrintWriter(new File("welcome.txt"))) { writer.println("Welcome message"); }
ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ, Ρ Π½Π°Ρ Π΅ΡΡΡ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΉ ΠΊΠΎΠ΄:
public <T extends Number> T add(T t) { T temp = t; ... return temp; }
Π ΡΡΠΎΠΌ ΡΠ»ΡΡΠ°Π΅, ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ var ΡΠ°Π±ΠΎΡΠ°Π΅Ρ ΠΊΠ°ΠΊ ΠΈ ΠΎΠΆΠΈΠ΄Π°Π»ΠΎΡΡ, ΡΠ°ΠΊ ΡΡΠΎ ΠΌΡ ΠΏΡΠΎΡΡΠΎ ΠΌΠΎΠΆΠ΅ΠΌ Π·Π°ΠΌΠ΅Π½ΠΈΡΡ T Π½Π° var :
public <T extends Number> T add(T t) { var temp = t; ... return temp; }
ΠΠ°Π²Π°ΠΉΡΠ΅ Π²Π·Π³Π»ΡΠ½Π΅ΠΌ Π½Π° Π΄ΡΡΠ³ΠΎΠΉ ΠΏΡΠΈΠΌΠ΅Ρ, Π³Π΄Π΅ ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ ΡΡΠΏΠ΅ΡΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ var :
codepublic <T extends Number> T add(T t) { List<T> numbers = new ArrayList<>(); numbers.add((T) Integer.valueOf(3)); numbers.add((T) Double.valueOf(3.9)); numbers.add(t); numbers.add("5"); // error: incompatible types: String cannot be converted to T ... }
Π’ΡΡ ΠΌΠΎΠΆΠ½ΠΎ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎ Π·Π°ΠΌΠ΅Π½ΠΈΡΡ List<T> Π½Π° var :
public <T extends Number> T add(T t) { var numbers = new ArrayList<T>(); // DON'T DO THIS, DON'T FORGET THE, T var numbers = new ArrayList<>(); numbers.add((T) Integer.valueOf(3)); numbers.add((T) Double.valueOf(3.9)); numbers.add(t); numbers.add("5"); // error: incompatible types: String cannot be converted to T ... }
ΠΠΎΠΆΠ½ΠΎ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ var ΡΠ°ΠΊΠΈΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ:
// explicit type Class<?> clazz = Integer.class; // use var var clazz = Integer.class;
ΠΠΎ Π½Π΅ Π·Π°ΠΌΠ΅Π½ΡΠΉΡΠ΅ Foo<?> Π½Π° var ΡΠΎΠ»ΡΠΊΠΎ ΠΏΠΎΡΠΎΠΌΡ, ΡΡΠΎ Π²Ρ ΠΈΠΌΠ΅Π΅ΡΠ΅ ΠΎΡΠΈΠ±ΠΊΠΈ Π² ΠΊΠΎΠ΄Π΅, Π° Ρ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠ΅ΠΌ var ΠΎΠ½ΠΈ ΡΡΠ΄Π΅ΡΠ½ΡΠΌ ΠΎΠ±ΡΠ°Π·ΠΎΠΌ ΠΈΡΡΠ΅Π·Π°ΡΡ.
ΠΠ°Π²Π°ΠΉΡΠ΅ ΡΠ°ΡΡΠΌΠΎΡΡΠΈΠΌ ΡΠ»Π΅Π΄ΡΡΡΠΈΠΉ ΠΏΡΠΈΠΌΠ΅Ρ ΠΊΠΎΠ΄Π°, ΠΎΠ½ Π½Π΅ Π·Π°Ρ Π²Π°ΡΡΠ²Π°ΡΡΠΈΠΉ, Π½ΠΎ, Π΄ΡΠΌΠ°Ρ, ΠΎΡΠ½ΠΎΠ²Π½ΡΡ ΠΈΠ΄Π΅Ρ Π²Ρ ΠΏΠΎΠΉΠΌΠ΅ΡΠ΅. ΠΡΡ ΠΎΠ΄Ρ ΠΈΠ· ΠΏΠ΅ΡΠ²ΠΎΠΉ ΡΡΡΠΎΠΊΠΈ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΄Π΅Π»Π°ΡΡ ΠΏΡΠ΅Π΄ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, ΡΡΠΎ Π²Ρ ΠΏΡΡΠ°Π»ΠΈΡΡ ΠΎΠΏΡΠ΅Π΄Π΅Π»ΠΈΡΡ ArrayList ΠΈΠ· ΡΡΡΠΎΠΊ, Π° Π² ΠΈΡΠΎΠ³Π΅ ΠΏΠΎΠ»ΡΡΠΈΠ»ΠΈ Collection<?> :
// explicit type Collection<?> stuff = new ArrayList<>(); stuff.add("hello"); // compile time error stuff.add("world"); // compile time error // use var, this will remove the error, but I don't think that this is // what you had in mind when you wrote the above code var stuff = new ArrayList<>(); strings.add("hello"); // no error strings.add("world"); // no error
ΠΡ Π·Π½Π°Π΅ΠΌ, ΡΡΠΎ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΄Π΅Π»Π°ΡΡ ΡΠ»Π΅Π΄ΡΡΡΠ΅Π΅:
// explicit type Class<? extends Number> intNumber = Integer.class; Class<? super FilterReader> fileReader = Reader.class;
ΠΡΠ»ΠΈ ΠΌΡ ΠΎΡΠΈΠ±ΠΎΡΠ½ΠΎ ΠΏΡΠΈΡΠ²ΠΎΠΈΠΌ Π½Π΅Π²Π΅ΡΠ½ΡΠΉ ΡΠΈΠΏ ΠΈ ΠΏΠΎΠ»ΡΡΠΈΠΌ ΠΎΡΠΈΠ±ΠΊΠΈ Π²ΠΎ Π²ΡΠ΅ΠΌΡ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΈΠΈ, ΡΡΠΎ Π±ΡΠ΄Π΅Ρ ΠΈΠΌΠ΅Π½Π½ΠΎ ΡΠΎ, ΡΠ΅Π³ΠΎ ΠΌΡ ΠΎΠΆΠΈΠ΄Π°Π΅ΠΌ:
// IT DOESN'T COMPILE // error: Class<Reader> cannot be converted to Class<? extends Number> Class<? extends Number> intNumber = Reader.class; // error: Class<Integer> cannot be converted to Class<? super FilterReader> Class<? super FilterReader> fileReader = Integer.class;
ΠΠΎ ΠΏΡΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠΈ var :
// using var var intNumber = Integer.class; var fileReader = Reader.class;
Π’Π΅ΠΏΠ΅ΡΡ ΠΌΡ ΠΌΠΎΠΆΠ΅ΠΌ Π½Π°Π·Π½Π°ΡΠΈΡΡ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠΌ Π»ΡΠ±ΠΎΠΉ ΠΊΠ»Π°ΡΡ, ΠΈ Π½Π°Ρ ΠΊΠΎΠ΄ Π±ΡΠ΄Π΅Ρ ΡΠΊΠΎΠΌΠΏΠΈΠ»ΠΈΡΠΎΠ²Π°Π½. ΠΠΎ ΡΡΠΎ Π½Π΅ ΡΠΎ ΡΠ΅Π³ΠΎ ΠΌΡ Ρ ΠΎΡΠ΅Π»ΠΈ β Π½Π°ΡΠΈ ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΠ΅ Π½Π΅ ΠΈΠΌΠ΅ΡΡ ΠΎΠ³ΡΠ°Π½ΠΈΡΠ΅Π½ΠΈΠΉ:
// this will compile just fine var intNumber = Reader.class; var fileReader = Integer.class;
Π ΡΡΠΎΠΉ ΡΡΠ°ΡΡΠ΅ ΠΌΡ ΡΠ°ΡΡΠΌΠΎΡΡΠ΅Π»ΠΈ ΡΠΈΠΏ Β« var Β», ΠΊΠΎΡΠΎΡΡΠΉ ΠΏΠΎΡΠ²ΠΈΠ»ΡΡ Π² Java 10. Π’Π°ΠΊΠΆΠ΅ ΡΠ°Π·ΠΎΠ±ΡΠ°Π»ΠΈ ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²ΠΎ ΠΏΡΠΈΠΌΠ΅ΡΠΎΠ², ΠΊΠΎΡΠΎΡΡΠ΅ Π΄Π΅ΠΌΠΎΠ½ΡΡΡΠΈΡΡΡΡ ΠΏΡΠ΅ΠΈΠΌΡΡΠ΅ΡΡΠ²Π° ΠΈ Π½Π΅Π΄ΠΎΡΡΠ°ΡΠΊΠΈ ΠΏΡΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠΈ Π΄ΠΈΠ½Π°ΠΌΠΈΡΠ΅ΡΠΊΠΎΠ³ΠΎ Π²ΡΠ²Π΅Π΄Π΅Π½ΠΈΡ ΡΠΈΠΏΠ° ΠΏΠ΅ΡΠ΅ΠΌΠ΅Π½Π½ΡΡ . Π Π½Π°ΠΊΠΎΠ½Π΅Ρ ΡΠ·Π½Π°Π»ΠΈ, ΡΡΠΎ ΠΏΡΠΎΠ²Π΅ΡΠΊΠ° ΡΠΈΠΏΠΎΠ² ΠΏΡΠΈ ΠΏΡΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠΈ var ΠΎΡΡΡΠ΅ΡΡΠ²Π»ΡΠ΅ΡΡΡ Π²ΠΎ Π²ΡΠ΅ΠΌΡ ΠΊΠΎΠΌΠΏΠΈΠ»ΡΡΠΈΠΈ, ΡΡΠΎ ΠΏΠΎΠ·Π²ΠΎΠ»ΡΠ΅Ρ ΠΎΡΠ»Π°Π²Π»ΠΈΠ²Π°ΡΡ ΠΌΠ½ΠΎΠΆΠ΅ΡΡΠ²ΠΎ ΠΎΡΠΈΠ±ΠΎΠΊ.
ΠΡΠΏΠΎΠ»ΡΠ·ΡΠΉΡΠ΅ var ΠΈ Π΄Π° ΠΏΡΠΈΠ±ΡΠ΄Π΅Ρ Ρ Π²Π°ΠΌΠΈ Java!
Source: https://habr.com/ru/post/438206/