26 recomendaciones para usar el tipo var en Java


La Inferencia de tipo variable local de Java (LVTI) o, brevemente, el tipo var (el identificador var no es una palabra clave, sino un nombre de tipo reservado) se agregó a Java 10 usando JEP 286: Inferencia de tipo variable local . Al ser una función de compilación del 100%, no afecta el código de bytes, el tiempo de ejecución o el rendimiento. Básicamente, el compilador verifica el lado derecho del operador de asignación y, en base a él, determina el tipo específico de la variable y luego lo reemplaza con var .


Además, es útil para reducir la verbosidad del código repetitivo, y también acelera el proceso de programación en sí. Por ejemplo, es muy conveniente escribir var evenAndOdd =... lugar de Map<Boolean, List<Integer>> evenAndOdd =...


La aparición de var no significa que siempre sea conveniente usarlo en todas partes, a veces será más práctico hacerlo con herramientas estándar.


En este artículo, veremos 26 situaciones, con ejemplos de cuándo puede usar var y cuándo no vale la pena.


Punto 1: intente dar nombres significativos a las variables locales


Por lo general, nos enfocamos en dar los nombres correctos a los campos de las clases, pero no prestamos la misma atención a los nombres de las variables locales. Cuando nuestros métodos se implementan perfectamente, contienen poco código y tienen buenos nombres, muy a menudo no prestamos atención a las variables locales, ni siquiera reducimos por completo sus nombres.


Cuando usamos var en lugar de escribir tipos explícitos, el compilador los detecta automáticamente y sustituye var . Pero, por otro lado, como resultado de esto, se hace más difícil para las personas leer y comprender el código, ya que el uso de var puede complicar su legibilidad y comprensión. En la mayoría de los casos, esto se debe a que tendemos a ver el tipo de una variable como información primaria y su nombre como secundario. Aunque debería ser todo lo contrario.


Ejemplo 1:


Muchos probablemente estarán de acuerdo en que en el siguiente ejemplo, los nombres de las variables locales son demasiado cortos:


 // HAVING public boolean callDocumentationTask() { DocumentationTool dtl = ToolProvider.getSystemDocumentationTool(); DocumentationTask dtt = dtl.getTask(...); return dtt.call(); } 

Cuando se usan nombres cortos, junto con var , el código se vuelve aún menos claro:


 // AVOID public boolean callDocumentationTask() { var dtl = ToolProvider.getSystemDocumentationTool(); var dtt = dtl.getTask(...); return dtt.call(); } 

Opción más preferida:


 // PREFER public boolean callDocumentationTask() { var documentationTool = ToolProvider.getSystemDocumentationTool(); var documentationTask = documentationTool.getTask(...); return documentationTask.call(); } 

Ejemplo 2


Evite nombrar variables como esta:


 // AVOID public List<Product> fetchProducts(long userId) { var u = userRepository.findById(userId); var p = u.getCart(); return p; } 

Use nombres más significativos:


 // PREFER public List<Product> fetchProducts(long userId) { var user = userRepository.findById(userId); var productList = user.getCart(); return productList; } 

Ejemplo 3


En un esfuerzo por dar nombres más comprensibles a las variables locales, no vaya a extremos:


 // AVOID var byteArrayOutputStream = new ByteArrayOutputStream(); 

En cambio, puede usar una opción más corta, pero no menos comprensible:


 // PREFER var outputStream = new ByteArrayOutputStream(); // or var outputStreamOfFoo = new ByteArrayOutputStream(); 

¿Sabía que Java tiene una clase interna llamada:
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState


Bueno, nombrar variables con este tipo puede ser complicado :)


Punto 2: use literales para ayudar a var a determinar el tipo de primitivo (int, long, float, double)


Sin el uso de literales para tipos primitivos, podemos encontrar que los tipos esperados e implícitos pueden diferir. Esto es causado por la conversión de tipo implícita utilizada por las variables var .


Por ejemplo, los siguientes dos fragmentos de código se comportan como se esperaba. Aquí declaramos explícitamente los tipos booleano y char :


 boolean flag = true; // this is of type boolean char a = 'a'; // this is of type char 

Ahora usamos var , en lugar de declarar explícitamente los tipos:


 var flag = true; // this is inferred as boolean var a = 'a'; // this is inferred as char 

Hasta ahora todo bien. Ahora haga lo mismo para los tipos int , long , float y 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 

Aunque el fragmento de código anterior es simple y directo, ahora usemos var en lugar de especificar explícitamente los tipos.


Evitar


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

Las cuatro variables se mostrarán como int . Para corregir este comportamiento, necesitamos usar literales Java:


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

Pero, ¿qué sucede si declaramos un número decimal?


Evite esto si espera obtener una variable de tipo float :


 // AVOID, IF THIS IS A FLOAT var floatNumber = 20.5; // this is inferred as double 

Para evitar sorpresas, use el literal apropiado:


 // PREFER, IF THIS IS A FLOAT var floatNumber = 20.5F; // this is inferred as float 

Punto 3: en algunos casos, las conversiones de tipo var e implícito pueden simplificar el soporte de código


Por ejemplo, supongamos que nuestro código está entre dos métodos. Un método obtiene un carrito de compras con diferentes productos y calcula el mejor precio. Para hacer esto, compara varios precios en el mercado y devuelve el precio total en forma de tipo flotante . Otro método simplemente deduce este precio de la tarjeta.


Primero, veamos un método que calcula el mejor precio:


 public float computeBestPrice(String[] items) { ... float price = ...; return price; } 

En segundo lugar, echemos un vistazo al método que funciona con el mapa:


 public boolean debitCard(float amount, ...) { ... } 

Ahora ponemos nuestro código entre estos dos métodos de servicio externo como cliente. Nuestros usuarios pueden elegir los productos para comprar, y calculamos el mejor precio para ellos, y luego cancelamos los fondos de la tarjeta:


 // AVOID public void purchaseCart(long customerId) { ... float price = computeBestPrice(...); debitCard(price, ...); } 

Después de un tiempo, la compañía propietaria de la API decide abandonar la representación material de los precios a favor del decimal (en lugar de flotante , ahora se usa int ). Entonces, modificaron el código API de la siguiente manera:


 public int computeBestPrice(String[] items) { ... float realprice = ...; ... int price = (int) realprice; return price; } public boolean debitCard(int amount, ...) { ... } 

El hecho es que nuestro código usa una declaración explícita de una variable flotante como precio. En su forma actual, recibiremos un error en el momento de la compilación. Pero si hubiéramos previsto tal situación y hubiéramos utilizado var en lugar de flotante , nuestro código continuaría funcionando sin problemas, gracias a la conversión de tipo implícita:


 // PREFER public void purchaseCart(long customerId) { ... var price = computeBestPrice(...); debitCard(price, ...); } 

Punto 4: cuando los literales no son una solución adecuada, use una conversión explícita o descarte var


Algunos tipos primitivos en Java no tienen literales especiales, por ejemplo, byte y tipos cortos . En este caso, usando la designación de tipo explícito, podemos crear variables sin ningún problema.


Use esto en lugar de var :


 // PREFER THIS INSTEAD OF USING VAR byte byteNumber = 45; // this is of type byte short shortNumber = 4533; // this is of type short 

Pero, ¿por qué en esta situación da preferencia a la notación de tipo explícito en lugar de simplemente usar var ? Bueno, escribamos este código usando var . Tenga en cuenta que en ambos casos, el compilador asumirá que necesita variables de tipo int .


Evita este error:


 // AVOID var byteNumber = 45; // this is inferred as int var shortNumber = 4533; // this is inferred as int 

Aquí no hay literales que nos ayuden, por lo tanto, nos vemos obligados a utilizar la conversión explícita de tipo descendente. Personalmente, evitaré tales situaciones, porque no veo ninguna ventaja aquí.


Solo use esta entrada si realmente quiere usar 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 

Punto 5: evite usar var si los nombres de las variables no contienen suficiente información de tipo para comprender el código


La ventaja de usar var es escribir código más conciso. Por ejemplo, en el caso de usar constructores, podemos evitar la necesidad de repetir el nombre de la clase y, por lo tanto, eliminar la redundancia de código.


Evita lo siguiente:


 // AVOID MemoryCacheImageInputStream inputStream = new MemoryCacheImageInputStream(...); 

Use en su lugar:


 // PREFER var inputStream = new MemoryCacheImageInputStream(...); 

Para la construcción a continuación, var también será una buena manera de simplificar el código sin perder información.


Evitar


 // AVOID JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager fm = compiler.getStandardFileManager(...); 

Utiliza el siguiente código:


 // PREFER var compiler = ToolProvider.getSystemJavaCompiler(); var fileManager = compiler.getStandardFileManager(...); 

Entonces, ¿por qué nos sentimos más cómodos trabajando con var en los ejemplos presentados? Porque toda la información necesaria está contenida en los nombres de las variables. Pero si var , en combinación con un nombre de variable, reduce la claridad del código, es mejor negarse a usarlo.


Evitar


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

Uso:


 // PREFER public File fetchCartContent() { return new File(...); } File cart = fetchCartContent(); 

Considere, por ejemplo, el uso de la clase java.nio.channels.Selector . Esta clase tiene un método estático open() que devuelve un nuevo selector y lo abre. Pero aquí puede pensar fácilmente que el método Selector.open() puede devolver un tipo booleano , dependiendo del éxito de abrir un selector existente, o incluso devolver un vacío . El uso de var aquí conducirá a la pérdida de información y la confusión en el código.


Punto 6: el tipo var garantiza la seguridad en tiempo de compilación


Esto significa que no podemos compilar una aplicación que intente realizar asignaciones incorrectas. Por ejemplo, el siguiente código no se compila:


 // IT DOESN'T COMPILE var items = 10; items = "10 items"; // incompatible types: String cannot be converted to int 

Pero este compila:


 var items = 10; items = 20; 

Y este código compila con éxito:


 var items = "10"; items = "10 items"; 

Una vez que el compilador ha definido el valor de la variable var , no podemos asignar nada más que este tipo.


Punto 7: var no se puede usar para crear instancias de un tipo específico y asignarlo a una variable de tipo de interfaz


En Java, utilizamos el enfoque de "programación con interfaces". Por ejemplo, creamos una instancia de la clase ArrayList, asociándola con una abstracción (interfaz):


 List<String> products = new ArrayList<>(); 

Y evitamos cosas como vincular un objeto a una variable del mismo tipo:


 ArrayList<String> products = new ArrayList<>(); 

Esta es la práctica más común y deseable, ya que podemos reemplazar fácilmente la implementación de la interfaz con cualquier otra. Para esto, solo es necesario declarar una variable de tipo de interfaz.


No podremos seguir este concepto usando variables var, como siempre se muestra un tipo específico para ellos. Por ejemplo, en el siguiente fragmento de código, el compilador determinará el tipo de la variable como ArrayList<String> :


 var productList = new ArrayList<String>(); // inferred as ArrayList<String> 

Hay varios argumentos de defensa que explican este comportamiento:


  • var se usa para variables locales, donde, en la mayoría de los casos, la programación usando interfaces se usa menos que en los casos con parámetros de método devueltos por valores o campos


  • El alcance de las variables locales debe ser pequeño, por lo que resolver los problemas causados ​​por cambiar a otra implementación no debería ser muy difícil.


  • var trata el código de la derecha como el inicializador utilizado para determinar el tipo real. Si, en algún momento, se cambia el inicializador, entonces el tipo que se está definiendo también puede cambiar, causando problemas en el código que se basa en esta variable.



Párrafo 8: la probabilidad de la conclusión de un tipo inesperado


El uso de var en combinación con un operador de diamante (<>) en ausencia de información para identificar el tipo puede conducir a resultados inesperados.


Antes de Java 7, se usaba la inferencia de tipo explícita para las colecciones:


 // explicitly specifying generic class's instantiation parameter type List<String> products = new ArrayList<String>(); 

Comenzando con Java 7, se introdujo el operador de diamantes . En este caso, el compilador derivará independientemente el tipo necesario:


 // inferring generic class's instantiation parameter type List<String> products = new ArrayList<>(); 

¿Qué tipo se mostrará en el siguiente código?


Debe evitar tales construcciones:


 // AVOID var productList = new ArrayList<>(); // is inferred as ArrayList<Object> 

El tipo se definirá como ArrayList<Object> . Esto se debe a que no se proporciona la información necesaria para determinar correctamente el tipo. Esto lleva al hecho de que se seleccionará el tipo más cercano, que puede ser compatible con el contexto de lo que está sucediendo. En este caso, Object .


Por lo tanto, var solo se puede usar si proporcionamos la información necesaria para determinar el tipo esperado. El tipo se puede especificar directamente o pasar como argumento.


Especifique directamente el tipo:


 // PREFER var productList = new ArrayList<String>(); // inferred as ArrayList<String> 

Pase argumentos del tipo requerido:


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

Elemento 9: asignar una matriz a una variable var no requiere corchetes []


Todos sabemos cómo declarar matrices en Java:


 int[] numbers = new int[5]; // or, less preferred int numbers[] = new int[5]; 

¿Qué tal usar var cuando se trabaja con matrices? En este caso, no es necesario usar soportes en el lado izquierdo.


Evite lo siguiente (esto ni siquiera se compila):


 // IT DOESN'T COMPILE var[] numbers = new int[5]; // or var numbers[] = new int[5]; 

Uso:


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

El siguiente código usando var también falla al compilar. Esto se debe a que el compilador no puede determinar el tipo desde el lado derecho:


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

Elemento 10: var no se puede usar al declarar múltiples variables en la misma línea


Si desea declarar variables del mismo tipo a la vez, debe saber que var no es adecuado para esto. El siguiente código no se compila:


 // IT DOESN'T COMPILE // error: 'var' is not allowed in a compound declaration var hello = "hello", bye = "bye", welcome = "welcome"; 

Use en su lugar:


 // PREFER String hello = "hello", bye = "bye", welcome = "welcome"; 

O es:


 // PREFER var hello = "hello"; var bye = "bye"; var welcome = "welcome"; 

Punto 11: las variables locales deben esforzarse por minimizar su alcance. El tipo var refuerza esta afirmación.


Mantenga un pequeño alcance para las variables locales: estoy seguro de que escuchó esta declaración antes de la var .


La legibilidad y la corrección rápida de errores son argumentos a favor de este enfoque. Por ejemplo, definamos una pila de la siguiente manera:


Evita esto:


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

Tenga en cuenta que estamos llamando al método forEach() , que se hereda de java.util.Vector . Este método pasará por la pila como cualquier otro vector y esto es lo que necesitamos. Pero ahora decidimos usar ArrayDeque lugar de Stack . Cuando hacemos esto, el método forEach() recibirá una implementación de ArrayDeque que atravesará la pila como una pila estándar (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(...); ... 

Esto no es lo que queremos. Es demasiado difícil rastrear el error aquí, porque el código que contiene la parte forEach() no se encuentra al lado del código en el que se realizaron los cambios. Para aumentar la velocidad de búsqueda y corrección de errores, es mucho mejor escribir código usando la variable stack , lo más cerca posible de la declaración de esta variable.


Esto se hace mejor de la siguiente manera:


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

Ahora, cuando el desarrollador cambie de Stack a ArrayQueue , podrá notar rápidamente el error y solucionarlo.


Cláusula 12: el tipo var simplifica el uso de varios tipos en operadores ternarios


Podemos usar diferentes tipos de operandos en el lado derecho del operador ternario.


Al especificar explícitamente los tipos, el siguiente código no se compila:


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

Sin embargo, podemos hacer esto:


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

El siguiente código tampoco se compila:


 // IT DOESN'T COMPILE int code = intOrString ? 12112 : "12112"; String code = intOrString ? 12112 : "12112"; 

Pero puedes usar tipos más generales:


 Serializable code = intOrString ? 12112 : "12112"; Object code = intOrString ? 12112 : "12112"; 

En todos estos casos, es mejor preferir 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"; 

De estos ejemplos no se deduce que el tipo var define tipos de objetos en tiempo de ejecución. ¡Esto no es así!


Y, por supuesto, el tipo var funcionará correctamente con los mismos tipos de ambos operandos:


 // inferred as float var code = oneOrTwoDigits ? 1211.2f : 1211.25f; 

Punto 13: el tipo var se puede usar dentro de los bucles


Podemos reemplazar fácilmente la declaración explícita de tipos en bucles for con el tipo var .


Cambiar un tipo int explícito 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 ... } 

Cambiar el tipo explícito de Order a var :


 List<Order> orderList = ...; // explicit type for (Order order : orderList) { ... } // using var for (var order : orderList) { // order type is inferred as Order ... } 

Punto 14: var funciona bien con flujos en Java 8


Es muy simple usar var de Java 10 con secuencias que aparecieron en Java 8.


Simplemente puede reemplazar la declaración explícita de tipo Stream con var :


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

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

Cláusula 15: var se puede usar al declarar variables locales destinadas a dividir grandes cadenas de expresiones en partes


Las expresiones con una gran cantidad de anidamiento se ven impresionantes y generalmente parecen algún tipo de código inteligente e importante. En el caso de que sea necesario facilitar la legibilidad del código, se recomienda dividir una expresión grande utilizando variables locales. Pero a veces escribir muchas variables locales parece un trabajo muy agotador que me gustaría evitar.


Un ejemplo de una gran expresión:


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

Es mejor dividir el código en sus componentes:


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

La segunda versión del código parece más legible y simple, pero la primera versión también tiene derecho a existir. Es absolutamente normal que nuestra mente se adapte a la comprensión de expresiones tan grandes y las prefiera a las variables locales. Sin embargo, usar el tipo var puede ayudar a dividir estructuras grandes al reducir el esfuerzo para declarar variables locales:


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

Cláusula 16: var no se puede usar como un tipo de retorno o como un tipo de argumento de método


Los dos fragmentos de código que se muestran a continuación no se compilarán.


Usando var como el tipo de retorno:


 // IT DOESN'T COMPILE public var countItems(Order order, long timestamp) { ... } 

Usando var como un tipo de argumento de método:


 // IT DOESN'T COMPILE public int countItems(var order, var timestamp) { ... } 

Cláusula 17: las variables locales de tipo var pueden pasarse como parámetros de un método o pueden tomar un valor de retorno


Los siguientes fragmentos de código se compilarán y funcionarán correctamente:


 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; 

Conclusión


« var », Java 10. , . , var , .


var Java!

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


All Articles