26 recomendações para usar o tipo var em Java


A Inferência de Tipo de Variável Local Java (LVTI) ou, brevemente, o tipo de var (o identificador de var não é uma palavra-chave, mas um nome de tipo reservado) foi adicionada ao Java 10 usando o JEP 286: Inferência de Tipo de Variável Local . Sendo uma função de compilador 100%, não afeta o bytecode, o tempo de execução ou o desempenho. Basicamente, o compilador verifica o lado direito do operador de atribuição e, com base nele, determina o tipo específico da variável e a substitui por var .


Além disso, é útil para reduzir a verbosidade do código padrão e também acelera o próprio processo de programação. Por exemplo, é muito conveniente escrever var evenAndOdd =... vez de Map<Boolean, List<Integer>> evenAndOdd =...


A aparência de var não significa que seja sempre e conveniente usá-lo em qualquer lugar; às vezes será mais prático fazer com ferramentas padrão.


Neste artigo, examinaremos 26 situações, com exemplos de quando você pode usar var e quando não vale a pena.


Ponto 1: tente atribuir nomes significativos às variáveis ​​locais


Normalmente, nos concentramos em fornecer os nomes corretos para os campos das classes, mas não prestamos a mesma atenção aos nomes das variáveis ​​locais. Quando nossos métodos são perfeitamente implementados, contêm pouco código e têm bons nomes, muitas vezes não prestamos atenção às variáveis ​​locais, nem reduzimos completamente seus nomes.


Quando usamos var em vez de escrever tipos explícitos, o compilador os detecta automaticamente e substitui var . Mas, por outro lado, como resultado disso, fica mais difícil para as pessoas lerem e entenderem o código, pois o uso de var pode complicar sua legibilidade e compreensão. Na maioria dos casos, isso ocorre porque tendemos a considerar o tipo de uma variável como informação primária e o nome como secundário. Embora deva ser exatamente o oposto.


Exemplo 1:


Muitos provavelmente concordam que no exemplo abaixo, os nomes das variáveis ​​locais são muito curtos:


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

Ao usar nomes abreviados, junto com var , o código fica ainda menos claro:


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

Opção mais preferida:


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

Exemplo 2:


Evite nomear variáveis ​​como esta:


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

Use nomes mais significativos:


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

Exemplo 3:


Em um esforço para atribuir nomes mais compreensíveis às variáveis ​​locais, não vá ao extremo:


 // AVOID var byteArrayOutputStream = new ByteArrayOutputStream(); 

Em vez disso, você pode usar uma opção mais curta, mas não menos compreensível:


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

Você sabia que o Java tem uma classe interna chamada:
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState


Bem, nomear variáveis ​​com esse tipo pode ser complicado :)


Ponto 2: use literais para ajudar var a identificar o tipo de primitivo (int, long, float, double)


Sem o uso de literais para tipos primitivos, podemos achar que os tipos esperado e implícito podem diferir. Isso é causado pela conversão implícita de tipo usada pelas variáveis var .


Por exemplo, os dois fragmentos de código a seguir se comportam conforme o esperado. Aqui declaramos explicitamente os tipos booleano e char :


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

Agora usamos var , em vez de declarar explicitamente os tipos:


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

Até agora tudo bem. Agora faça o mesmo para os tipos int , long , float e 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 

Embora o snippet de código acima seja simples e direto, agora vamos usar var em vez de especificar explicitamente os tipos.


Evite:


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

Todas as quatro variáveis ​​serão exibidas como int . Para corrigir esse comportamento, precisamos usar literais 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 

Mas o que acontece se declararmos um número decimal?


Evite isso se você espera obter uma variável do tipo float :


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

Para evitar surpresa, use o literal apropriado:


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

Ponto 3: em alguns casos, conversões de tipo var e implícitas podem simplificar o suporte ao código


Por exemplo, vamos supor que nosso código esteja entre dois métodos. Um método obtém um carrinho de compras com produtos diferentes e calcula o melhor preço. Para fazer isso, ele compara vários preços no mercado e retorna o preço total na forma de um tipo flutuante . Outro método simplesmente deduz esse preço do cartão.


Primeiro, vejamos um método que calcula o melhor preço:


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

Em segundo lugar, vamos dar uma olhada no método que funciona com o mapa:


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

Agora, colocamos nosso código entre esses dois métodos de serviço externos como cliente. Nossos usuários podem escolher as mercadorias a serem compradas e calculamos o melhor preço para elas e, em seguida, baixamos os fundos do cartão:


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

Após algum tempo, a empresa proprietária da API decide abandonar a representação material dos preços em favor do decimal (em vez de float , int agora é usado). Então, eles modificaram o código da API da seguinte maneira:


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

O fato é que nosso código usa uma declaração explícita de uma variável float como preço. Em sua forma atual, receberemos um erro no momento da compilação. Mas se tivéssemos previsto tal situação e usado var em vez de float , nosso código continuaria funcionando sem problemas, graças à conversão implícita de tipo:


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

Ponto 4: quando literais não são uma solução adequada, use conversão explícita ou descartar var


Alguns tipos primitivos em Java não possuem literais especiais, por exemplo, bytes e tipos curtos . Nesse caso, usando a designação explícita de tipo, podemos criar variáveis ​​sem problemas.


Use isso em vez de var :


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

Mas por que nessa situação dar preferência à notação de tipo explícita em vez de apenas usar var ? Bem, vamos escrever esse código usando var . Observe que nos dois casos, o compilador assumirá que você precisa de variáveis ​​do tipo int .


Evite este erro:


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

Não há literais aqui que viriam em nosso auxílio, portanto somos forçados a usar a conversão explícita de tipo descendente. Pessoalmente, evitarei essas situações, porque não vejo vantagens aqui.


Use esta entrada apenas se você realmente quiser 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 

Ponto 5: evite usar var se os nomes de variáveis ​​não contiverem informações de tipo suficientes para entender o código


A vantagem de usar var é escrever um código mais conciso. Por exemplo, no caso de usar construtores, podemos evitar a necessidade de repetir o nome da classe e, portanto, eliminar a redundância de código.


Evite o seguinte:


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

Use em vez disso:


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

Para a construção abaixo, var também será uma boa maneira de simplificar o código sem perder informações.


Evite:


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

Use o seguinte código:


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

Então, por que estamos mais à vontade trabalhando com var nos exemplos apresentados? Porque toda a informação necessária está contida nos nomes das variáveis. Mas se var , em combinação com um nome de variável, reduz a clareza do código, é melhor recusar usá-lo.


Evite:


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

Considere, por exemplo, o uso da classe java.nio.channels.Selector . Esta classe possui um método estático open() que retorna um novo Seletor e o abre. Mas aqui você pode pensar facilmente que o método Selector.open() pode retornar um tipo booleano , dependendo do sucesso de abrir um seletor existente ou até mesmo retornar nulo . Usar var aqui levará à perda de informações e confusão no código.


Ponto 6: o tipo var garante segurança em tempo de compilação


Isso significa que não podemos compilar um aplicativo que tente executar atribuições incorretas. Por exemplo, o código abaixo não compila:


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

Mas este compila:


 var items = 10; items = 20; 

E esse código compila com sucesso:


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

Depois que o compilador definiu o valor da variável var , não podemos atribuir nada além desse tipo.


Ponto 7: var não pode ser usado para instanciar um tipo específico e atribuí-lo a uma variável do tipo de interface


Em Java, usamos a abordagem "programação com interfaces". Por exemplo, criamos uma instância da classe ArrayList, associando-a a uma abstração (interface):


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

E evitamos coisas como vincular um objeto a uma variável do mesmo tipo:


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

Essa é a prática mais comum e desejável, pois podemos substituir facilmente a implementação da interface por qualquer outra. Para isso, é necessário apenas declarar uma variável do tipo de interface.


Não seremos capazes de seguir esse conceito usando variáveis ​​var, pois um tipo específico é sempre exibido para eles. Por exemplo, no seguinte fragmento de código, o compilador determinará o tipo da variável como ArrayList<String> :


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

Existem vários argumentos de defesa que explicam esse comportamento:


  • var é usado para variáveis ​​locais, onde, na maioria dos casos, a programação usando interfaces é usada menos do que nos casos com parâmetros de método retornados por valores ou campos


  • O escopo das variáveis ​​locais deve ser pequeno, portanto, resolver problemas causados ​​pela mudança para outra implementação não deve ser muito difícil.


  • var trata o código à direita como o inicializador usado para determinar o tipo real. Se, em algum momento, o inicializador for alterado, o tipo que está sendo definido também poderá ser alterado, causando problemas no código que depende dessa variável.



Parágrafo 8: probabilidade de conclusão de um tipo inesperado


O uso de var em combinação com um operador de diamante (<>) na ausência de informações para identificar o tipo pode levar a resultados inesperados.


Antes do Java 7, a inferência explícita de tipo era usada para coleções:


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

A partir do Java 7, o operador diamante foi introduzido. Nesse caso, o compilador derivará independentemente o tipo necessário:


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

Que tipo será exibido no código abaixo?


Você deve evitar essas construções:


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

O tipo será definido como ArrayList<Object> . Isso ocorre porque as informações necessárias para determinar corretamente o tipo não são fornecidas. Isso leva ao fato de que o tipo mais próximo será selecionado, o que pode ser compatível com o contexto do que está acontecendo. Nesse caso, Object .


Assim, var só pode ser usado se fornecermos as informações necessárias para determinar o tipo esperado. O tipo pode ser especificado diretamente ou passado como argumento.


Especifique diretamente o tipo:


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

Passe argumentos do tipo necessário:


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

Item 9: atribuir uma matriz a uma variável var não requer colchetes []


Todos nós sabemos como declarar matrizes em Java:


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

Que tal usar var ao trabalhar com matrizes? Nesse caso, não há necessidade de usar colchetes no lado esquerdo.


Evite o seguinte (isso nem compila):


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

O código abaixo usando var também falha na compilação. Isso ocorre porque o compilador não pode determinar o tipo do lado direito:


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

Item 10: var não pode ser usado ao declarar várias variáveis ​​na mesma linha


Se você deseja declarar variáveis ​​do mesmo tipo de uma só vez, precisa saber que var não é adequado para isso. O código a seguir não compila:


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

Use em vez disso:


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

Ou é:


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

Ponto 11: variáveis ​​locais devem se esforçar para minimizar seu escopo. O tipo var reforça essa afirmação.


Mantenha um pequeno escopo para variáveis ​​locais - tenho certeza de que você ouviu essa declaração antes de var .


A legibilidade e as correções rápidas de bugs são argumentos a favor dessa abordagem. Por exemplo, vamos definir uma pilha da seguinte maneira:


Evite isso:


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

Observe que estamos chamando o método forEach() , herdado de java.util.Vector . Este método percorrerá a pilha como qualquer outro vetor e é disso que precisamos. Mas agora decidimos usar ArrayDeque vez de Stack . Quando fazemos isso, o método forEach() receberá uma implementação de ArrayDeque que percorrerá a pilha como uma pilha padrão (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(...); ... 

Não é isso que queremos. É muito difícil rastrear o erro aqui, porque o código que contém a parte forEach() não está localizado próximo ao código no qual as alterações foram feitas. Para aumentar a velocidade de busca e correção de erros, é muito melhor escrever código usando a variável de stack , o mais próximo possível da declaração dessa variável.


É melhor fazer o seguinte:


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

Agora, quando o desenvolvedor alternar de Stack para ArrayQueue , ele poderá perceber rapidamente o erro e corrigi-lo.


Cláusula 12: o tipo var simplifica o uso de vários tipos em operadores ternários


Podemos usar diferentes tipos de operandos no lado direito do operador ternário.


Ao especificar explicitamente tipos, o código a seguir não é compilado:


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

No entanto, podemos fazer isso:


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

O código abaixo também não compila:


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

Mas você pode usar tipos mais gerais:


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

Em todos esses casos, é melhor 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"; 

A partir desses exemplos, não se segue que o tipo var define os tipos de objeto em tempo de execução. Isto não é assim!


E, é claro, o tipo var funcionará corretamente com os mesmos tipos de ambos os operandos:


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

Ponto 13: o tipo var pode ser usado dentro de loops


Podemos facilmente substituir a declaração explícita de types in for loops pelo tipo var .


Alterando um tipo int explícito para 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 ... } 

Alterando o tipo explícito de Order para var :


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

Ponto 14: var funciona bem com fluxos em Java 8


É muito simples usar var do Java 10 com fluxos que apareceram no Java 8.


Você pode simplesmente substituir a declaração explícita do tipo Stream por var :


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

Exemplo 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 pode ser usada ao declarar variáveis ​​locais destinadas a quebrar grandes cadeias de expressões em partes


Expressões com muitos aninhamentos parecem impressionantes e geralmente parecem algum tipo de código inteligente e importante. No caso em que é necessário facilitar a legibilidade do código, é recomendável dividir uma expressão grande usando variáveis ​​locais. Mas, às vezes, escrever muitas variáveis ​​locais parece um trabalho muito exaustivo que eu gostaria de evitar.


Um exemplo de uma expressão grande:


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

Melhor dividir o código em seus 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(); 

A segunda versão do código parece mais legível e mais simples, mas a primeira versão também tem o direito de existir. É absolutamente normal que nossa mente se adapte ao entendimento de expressões tão grandes e as prefira a variáveis ​​locais. No entanto, o uso do tipo var pode ajudar a dividir grandes estruturas, reduzindo o esforço para declarar variáveis ​​locais:


 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 não pode ser usada como um tipo de retorno ou como um tipo de argumento de método


Os dois trechos de código mostrados abaixo não serão compilados.


Usando var como o tipo de retorno:


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

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


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

Cláusula 17: variáveis ​​locais do tipo var podem ser passadas como parâmetros do método ou podem assumir o valor retornado pelo método


Os seguintes fragmentos de código serão compilados e funcionarão corretamente:


 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; 

Conclusão


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


var Java!

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


All Articles