πŸ“œ ⬆️ ⬇️

26 recommendations for using the var type in Java


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.


Point 1: try to give meaningful names to local variables


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.


Example 1:


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(); } 

Example 2:


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; } 

Example 3:


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 :)


Point 2: use literals to help var determine the exact primitive type (int, long, float, double)


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 

Point 3: in some cases, var and implicit type conversions can simplify code maintenance.


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, ...); } 

Point 4: when literals are not the right solution, use explicit type casts or discard var


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 

Step 5: Avoid using var if variable names do not contain sufficient type information to understand the code.


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.


Point 6: the var type guarantees security at compile time


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.


Item 7: var cannot be used to create an instance of a particular type and assign it to an interface type variable


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:



Point 8: the probability of outputting an unexpected type


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); 

Point 9: assigning an array to a var-variable does not require brackets []


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}; 

Paragraph 10: var cannot be used when declaring several variables in one line.


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"; 

Point 11: local variables should aim to minimize their scope. The var type reinforces this statement.


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.


Paragraph 12: the var type simplifies the use of various types in ternary operators.


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; 

Paragraph 13: the var type can be used inside loops.


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 ... } 

Point 14: var works fine with streams in Java 8


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 :


Example 1:


 // 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); 

Example 2:


 // 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> 

Paragraph 15: var can be used when declaring local variables intended for splitting large chains of expressions into parts


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(); 

Clause 16: var cannot be used as a return type or as a method argument type


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) { ... } 

Paragraph 17: local variables of the var type can be passed as method parameters or can accept the value returned by the method.


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 

ΠŸΡƒΠ½ΠΊΡ‚ 18: ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ var ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Π½Ρ‹ с Π°Π½ΠΎΠ½ΠΈΠΌΠ½Ρ‹ΠΌΠΈ классами


ВмСсто явного указания Ρ‚ΠΈΠΏΠΎΠ²:


 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); 

ΠŸΡƒΠ½ΠΊΡ‚ 19: ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ Ρ‚ΠΈΠΏΠ° var ΠΌΠΎΠ³ΡƒΡ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒΡΡ Π² качСствС effectively final ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ…


Вспомним, Ρ‡Ρ‚ΠΎ:


… начиная с 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 

ΠŸΡƒΠ½ΠΊΡ‚ 20: var-ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Π΅ ΠΌΠΎΠ³ΡƒΡ‚ Π±Ρ‹Ρ‚ΡŒ final-ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹ΠΌΠΈ


Π˜Π·Π½Π°Ρ‡Π°Π»ΡŒΠ½ΠΎ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ 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 } } 

ΠŸΡƒΠ½ΠΊΡ‚ 21: лямбда выраТСниям ΠΈ ссылкам Π½Π° ΠΌΠ΅Ρ‚ΠΎΠ΄Ρ‹ Π½ΡƒΠΆΠ½Ρ‹ явныС Ρ‚ΠΈΠΏΡ‹


Π’ΠΈΠΏ 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 

ΠŸΡƒΠ½ΠΊΡ‚ 22: ΠΈΠ½ΠΈΡ†ΠΈΠ°Π»ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ var null'Π΅ΠΌ Π·Π°ΠΏΡ€Π΅Ρ‰Π΅Π½ΠΎ


Π—Π°ΠΏΡ€Π΅Ρ‰Π΅Π½ΠΎ ΠΎΠ±ΡŠΡΠ²Π»ΡΡ‚ΡŒ 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"; 

ΠŸΡƒΠ½ΠΊΡ‚ 23: Ρ‚ΠΈΠΏ var нСльзя ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² полях класса


Π’Ρ‹ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ 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; ... } 

ΠŸΡƒΠ½ΠΊΡ‚ 24: var нСльзя ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π² Π±Π»ΠΎΠΊΠ΅ catch


Π’Π΅ΠΌ Π½Π΅ ΠΌΠ΅Π½Π΅Π΅, это Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΎ Π² try-with-resources


Π‘Π»ΠΎΠΊ catch


Когда ΠΊΠΎΠ΄ бросаСт ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅, ΠΌΡ‹ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΠΏΠΎΠΉΠΌΠ°Ρ‚ΡŒ Π΅Π³ΠΎ, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΉ Ρ‚ΠΈΠΏ.


Π‘Π»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΉ ΠΊΠΎΠ΄ Π²Ρ‹Π·ΠΎΠ²Π΅Ρ‚ ΠΎΡˆΠΈΠ±ΠΊΡƒ компиляции:


 // IT DOESN'T COMPILE try { TimeUnit.NANOSECONDS.sleep(5000); } catch (var ex) { ... } 

Π’ Ρ‚Π°ΠΊΠΎΠΌ случаС Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ явный Ρ‚ΠΈΠΏ ΠΈΡΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ:


 // PREFER try { TimeUnit.NANOSECONDS.sleep(5000); } catch (InterruptedException ex) { ... } 

Try-with-resources


Однако, 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"); } 

ΠŸΡƒΠ½ΠΊΡ‚ 25: Ρ‚ΠΈΠΏ var ΠΌΠΎΠΆΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ с Π΄ΠΆΠ΅Π½Π΅Ρ€ΠΈΠΊΠ°ΠΌΠΈ


НапримСр, Ρƒ нас Π΅ΡΡ‚ΡŒ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰ΠΈΠΉ ΠΊΠΎΠ΄:


 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 ... } 

ΠŸΡƒΠ½ΠΊΡ‚ 26: Π±ΡƒΠ΄ΡŒΡ‚Π΅ Π²Π½ΠΈΠΌΠ°Ρ‚Π΅Π»ΡŒΠ½Ρ‹ с Ρ‚ΠΈΠΏΠΎΠΌ var ΠΏΡ€ΠΈ использовании Wildcards (?), ΠΊΠΎΠ²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² ΠΈ ΠΊΠΎΠ½Ρ‚Ρ€Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ²


ИспользованиС? Wildcards


МоТно бСзопасно ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ 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 

ИспользованиС ΠΊΠΎΠ²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² (Foo <? extends T>) ΠΈ ΠΊΠΎΠ½Ρ‚Ρ€Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² (Foo <? super T>)


ΠœΡ‹ Π·Π½Π°Π΅ΠΌ, Ρ‡Ρ‚ΠΎ ΠΌΠΎΠΆΠ½ΠΎ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅Π΅:


 // 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; 

Conclusion


Π’ этой ΡΡ‚Π°Ρ‚ΡŒΠ΅ ΠΌΡ‹ рассмотрСли Ρ‚ΠΈΠΏ Β« var Β», ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ появился Π² Java 10. Π’Π°ΠΊΠΆΠ΅ Ρ€Π°Π·ΠΎΠ±Ρ€Π°Π»ΠΈ мноТСство ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΎΠ², ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π΄Π΅ΠΌΠΎΠ½ΡΡ‚Ρ€ΠΈΡ€ΡƒΡŽΡ‚ прСимущСства ΠΈ нСдостатки ΠΏΡ€ΠΈ использовании динамичСского вывСдСния Ρ‚ΠΈΠΏΠ° ΠΏΠ΅Ρ€Π΅ΠΌΠ΅Π½Π½Ρ‹Ρ…. И Π½Π°ΠΊΠΎΠ½Π΅Ρ† ΡƒΠ·Π½Π°Π»ΠΈ, Ρ‡Ρ‚ΠΎ ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ° Ρ‚ΠΈΠΏΠΎΠ² ΠΏΡ€ΠΈ ΠΏΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠΈ var осущСствляСтся Π²ΠΎ врСмя компиляции, Ρ‡Ρ‚ΠΎ позволяСт ΠΎΡ‚Π»Π°Π²Π»ΠΈΠ²Π°Ρ‚ΡŒ мноТСство ошибок.


Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ var ΠΈ Π΄Π° ΠΏΡ€ΠΈΠ±ΡƒΠ΄Π΅Ρ‚ с Π²Π°ΠΌΠΈ Java!



Source: https://habr.com/ru/post/438206/