26 Empfehlungen für die Verwendung des var-Typs in Java


Die Java Local Variable Type Inference (LVTI) oder kurz der Var- Typ (der Var- Bezeichner ist kein Schlüsselwort, sondern ein reservierter Typname) wurde Java 10 mithilfe von JEP 286: Local-Variable Type Inference hinzugefügt. Da es sich um eine 100% ige Compilerfunktion handelt, hat dies keinen Einfluss auf Bytecode, Laufzeit oder Leistung. Grundsätzlich überprüft der Compiler die rechte Seite des Zuweisungsoperators und ermittelt darauf basierend den spezifischen Typ der Variablen und ersetzt ihn dann durch var .


Darüber hinaus ist es nützlich, um die Ausführlichkeit von Boilerplate-Code zu reduzieren und den Programmierprozess selbst zu beschleunigen. Zum Beispiel ist es sehr praktisch, var evenAndOdd =... anstelle von Map<Boolean, List<Integer>> evenAndOdd =... zu schreiben Map<Boolean, List<Integer>> evenAndOdd =...


Das Aussehen von var bedeutet nicht, dass es immer und bequem ist, es überall zu verwenden. Manchmal ist es praktischer, mit Standardwerkzeugen zu arbeiten.


In diesem Artikel werden 26 Situationen betrachtet, mit Beispielen, wann Sie var verwenden können und wann es sich nicht lohnt.


Punkt 1: Versuchen Sie, lokalen Variablen aussagekräftige Namen zu geben


Normalerweise konzentrieren wir uns darauf, den Feldern der Klassen die richtigen Namen zu geben, aber wir widmen den Namen lokaler Variablen nicht die gleiche Aufmerksamkeit. Wenn unsere Methoden perfekt implementiert sind, wenig Code enthalten und gute Namen haben, achten wir sehr oft nicht auf lokale Variablen oder reduzieren ihre Namen sogar vollständig.


Wenn wir var verwenden, anstatt explizite Typen zu schreiben, erkennt der Compiler diese automatisch und ersetzt var . Andererseits wird es dadurch für die Menschen schwieriger, den Code zu lesen und zu verstehen, da die Verwendung von var seine Lesbarkeit und sein Verständnis erschweren kann. In den meisten Fällen liegt dies daran, dass wir den Typ einer Variablen als primäre Information und ihren Namen als sekundäre betrachten. Obwohl es genau das Gegenteil sein sollte.


Beispiel 1:


Viele werden wahrscheinlich zustimmen, dass im folgenden Beispiel die Namen lokaler Variablen zu kurz sind:


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

Bei Verwendung von Kurznamen zusammen mit var wird der Code noch weniger klar:


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

Bevorzugtere Option:


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

Beispiel 2:


Vermeiden Sie es, Variablen wie folgt zu benennen:


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

Verwenden Sie aussagekräftigere Namen:


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

Beispiel 3:


Gehen Sie nicht zu Extremen, um lokalen Variablen verständlichere Namen zu geben:


 // AVOID var byteArrayOutputStream = new ByteArrayOutputStream(); 

Stattdessen können Sie eine kürzere, aber nicht weniger verständliche Option verwenden:


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

Wussten Sie, dass Java eine innere Klasse namens hat?
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState


Das Benennen von Variablen mit diesem Typ kann schwierig sein :)


Punkt 2: Verwenden Sie Literale, um var dabei zu helfen, den Typ des Grundelements zu bestimmen (int, long, float, double).


Ohne die Verwendung von Literalen für primitive Typen können wir feststellen, dass erwartete und implizite Typen unterschiedlich sein können. Dies wird durch die implizite Typkonvertierung verursacht, die von var- Variablen verwendet wird.


Beispielsweise verhalten sich die folgenden zwei Codefragmente wie erwartet. Hier deklarieren wir explizit die Typen Boolean und Char :


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

Jetzt verwenden wir var , anstatt explizit Typen zu deklarieren:


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

So weit so gut. Machen Sie jetzt dasselbe für die Typen int , long , float und 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 

Obwohl das obige Code-Snippet einfach und unkompliziert ist, verwenden wir jetzt var, anstatt explizit Typen anzugeben.


Vermeiden Sie:


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

Alle vier Variablen werden als int ausgegeben. Um dieses Verhalten zu beheben, müssen wir Java-Literale verwenden:


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

Aber was passiert, wenn wir eine Dezimalzahl deklarieren?


Vermeiden Sie dies, wenn Sie eine Variable vom Typ float erwarten:


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

Verwenden Sie das entsprechende Literal, um Überraschungen zu vermeiden:


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

Punkt 3: In einigen Fällen können var- und implizite Typkonvertierungen die Codeunterstützung vereinfachen


Nehmen wir zum Beispiel an, dass unser Code zwischen zwei Methoden liegt. Eine Methode erhält einen Einkaufswagen mit verschiedenen Produkten und berechnet den besten Preis. Dazu vergleicht er verschiedene Marktpreise und gibt den Gesamtpreis in Form eines Float- Typs zurück. Eine andere Methode zieht diesen Preis einfach von der Karte ab.


Schauen wir uns zunächst eine Methode an, mit der der beste Preis berechnet wird:


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

Zweitens werfen wir einen Blick auf die Methode, die mit der Karte funktioniert:


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

Jetzt setzen wir unseren Code als Client zwischen diese beiden externen Servicemethoden. Unsere Benutzer können die zu kaufenden Waren auswählen und wir berechnen den besten Preis für sie und schreiben dann das Geld von der Karte ab:


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

Nach einiger Zeit beschließt das Unternehmen, dem die API gehört, die wesentliche Darstellung der Preise zugunsten der Dezimalstelle aufzugeben (anstelle von float wird jetzt int verwendet). Daher haben sie den API-Code wie folgt geändert:


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

Tatsache ist, dass unser Code eine explizite Deklaration einer Float- Variablen als Preis verwendet. In der aktuellen Form erhalten wir beim Kompilieren einen Fehler. Wenn wir jedoch eine solche Situation vorausgesehen und var anstelle von float verwendet hätten , würde unser Code dank der impliziten Typkonvertierung ohne Probleme weiter funktionieren:


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

Punkt 4: Wenn Literale keine geeignete Lösung sind, verwenden Sie explizites Casting oder verwerfen Sie var


Einige primitive Typen in Java haben keine speziellen Literale, z. B. Byte- und Kurztypen. In diesem Fall können wir mithilfe der expliziten Typbezeichnung problemlos Variablen erstellen.


Verwenden Sie dies anstelle von var :


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

Aber warum bevorzugen Sie in dieser Situation die explizite Typennotation, anstatt nur var zu verwenden ? Nun, schreiben wir diesen Code mit var . Beachten Sie, dass der Compiler in beiden Fällen davon ausgeht, dass Sie Variablen vom Typ int benötigen.


Vermeiden Sie diesen Fehler:


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

Es gibt hier keine Literale, die uns helfen könnten, daher sind wir gezwungen, eine explizite Abwärtskonvertierung zu verwenden. Persönlich werde ich solche Situationen vermeiden, da ich hier keine Vorteile sehe.


Verwenden Sie diesen Eintrag nur, wenn Sie var wirklich verwenden möchten:


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

Punkt 5: Vermeiden Sie die Verwendung von var, wenn Variablennamen nicht genügend Typinformationen enthalten, um den Code zu verstehen


Der Vorteil der Verwendung von var besteht darin, präziseren Code zu schreiben. Im Fall der Verwendung von Konstruktoren können wir beispielsweise vermeiden, dass der Klassenname wiederholt werden muss, und daher die Code-Redundanz beseitigen.


Vermeiden Sie Folgendes:


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

Verwenden Sie stattdessen:


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

Für die folgende Konstruktion ist var auch eine gute Möglichkeit, den Code zu vereinfachen, ohne Informationen zu verlieren.


Vermeiden Sie:


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

Verwenden Sie den folgenden Code:


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

Warum arbeiten wir in den vorgestellten Beispielen besser mit var ? Weil alle notwendigen Informationen in den Namen der Variablen enthalten sind. Wenn var jedoch in Kombination mit einem Variablennamen die Klarheit des Codes verringert, ist es besser, die Verwendung abzulehnen.


Vermeiden Sie:


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

Verwendung:


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

Betrachten Sie beispielsweise die Verwendung der Klasse java.nio.channels.Selector . Diese Klasse verfügt über eine statische open() -Methode, die einen neuen Selector zurückgibt und ihn öffnet. Aber hier können Sie leicht denken, dass die Selector.open() -Methode einen booleschen Typ zurückgeben kann, abhängig vom Erfolg beim Öffnen eines vorhandenen Selektors, oder sogar void zurückgeben kann . Die Verwendung von var führt hier zu Informationsverlust und Verwirrung im Code.


Punkt 6: var type garantiert die Sicherheit bei der Kompilierung


Dies bedeutet, dass wir keine Anwendung kompilieren können, die versucht, falsche Zuweisungen auszuführen. Der folgende Code wird beispielsweise nicht kompiliert:


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

Aber dieser kompiliert:


 var items = 10; items = 20; 

Und dieser Code wird erfolgreich kompiliert:


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

Sobald der Compiler den Wert der Variablen var definiert hat, können wir nichts anderes als diesen Typ zuweisen.


Punkt 7: var kann nicht verwendet werden, um einen bestimmten Typ zu instanziieren und ihn einer Schnittstellentypvariablen zuzuweisen


In Java verwenden wir den Ansatz "Programmieren mit Schnittstellen". Zum Beispiel erstellen wir eine Instanz der ArrayList-Klasse und ordnen sie einer Abstraktion (Schnittstelle) zu:


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

Und wir vermeiden Dinge wie das Binden eines Objekts an eine Variable des gleichen Typs:


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

Dies ist die häufigste und wünschenswerteste Vorgehensweise, da wir die Schnittstellenimplementierung problemlos durch eine andere ersetzen können. Dazu muss nur eine Schnittstellentypvariable deklariert werden.


Wir werden diesem Konzept nicht folgen können, wenn wir var-Variablen verwenden, as Für sie wird immer ein bestimmter Typ angezeigt. Im folgenden Codeausschnitt bestimmt der Compiler beispielsweise den Typ der Variablen als ArrayList<String> :


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

Es gibt mehrere Verteidigungsargumente, die dieses Verhalten erklären:


  • var wird für lokale Variablen verwendet, wobei in den meisten Fällen die Programmierung über Schnittstellen weniger verwendet wird als in Fällen mit Methodenparametern, die von Werten oder Feldern zurückgegeben werden


  • Der Umfang lokaler Variablen sollte klein sein, daher sollte die Lösung von Problemen, die durch den Wechsel zu einer anderen Implementierung verursacht werden, nicht sehr schwierig sein


  • var behandelt den Code auf der rechten Seite als Initialisierer, mit dem der tatsächliche Typ bestimmt wird. Wenn irgendwann der Initialisierer geändert wird, kann sich auch der zu definierende Typ ändern, was zu Problemen im Code führt, der auf dieser Variablen beruht.



Absatz 8: Die Wahrscheinlichkeit des Abschlusses eines unerwarteten Typs


Die Verwendung von var in Kombination mit einem Diamantoperator (<>) ohne Informationen zur Identifizierung des Typs kann zu unerwarteten Ergebnissen führen.


Vor Java 7 wurde für Sammlungen eine explizite Typinferenz verwendet:


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

Beginnend mit Java 7 wurde der Diamantoperator eingeführt. In diesem Fall leitet der Compiler unabhängig den erforderlichen Typ ab:


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

Welcher Typ wird im folgenden Code ausgegeben?


Sie sollten solche Konstruktionen vermeiden:


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

Der Typ wird als ArrayList<Object> . Dies liegt daran, dass die zur korrekten Bestimmung des Typs erforderlichen Informationen nicht bereitgestellt werden. Dies führt dazu, dass der nächstgelegene Typ ausgewählt wird, der mit dem Kontext des Geschehens kompatibel sein kann. In diesem Fall Object .


Daher kann var nur verwendet werden, wenn wir die erforderlichen Informationen zur Bestimmung des erwarteten Typs bereitstellen. Der Typ kann direkt angegeben oder als Argument übergeben werden.


Geben Sie direkt den Typ an:


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

Übergeben Sie Argumente des erforderlichen Typs:


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

Punkt 9: Das Zuweisen eines Arrays zu einer var-Variablen erfordert keine Klammern []


Wir alle wissen, wie man Arrays in Java deklariert:


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

Wie wäre es mit var bei der Arbeit mit Arrays? In diesem Fall müssen keine Klammern auf der linken Seite verwendet werden.


Vermeiden Sie Folgendes (dies wird nicht einmal kompiliert):


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

Verwendung:


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

Der folgende Code mit var kann ebenfalls nicht kompiliert werden. Dies liegt daran, dass der Compiler den Typ nicht von der rechten Seite bestimmen kann:


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

Punkt 10: var kann nicht verwendet werden, wenn mehrere Variablen in derselben Zeile deklariert werden


Wenn Sie Variablen desselben Typs gleichzeitig deklarieren möchten, müssen Sie wissen, dass var dafür nicht geeignet ist. Der folgende Code wird nicht kompiliert:


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

Verwenden Sie stattdessen:


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

Oder ist es:


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

Punkt 11: Lokale Variablen sollten sich bemühen, ihren Umfang zu minimieren. Der var-Typ verstärkt diese Aussage.


Halten Sie einen kleinen Bereich für lokale Variablen ein - ich bin sicher, Sie haben diese Anweisung vor var gehört .


Lesbarkeit und schnelle Fehlerbehebungen sprechen für diesen Ansatz. Definieren wir beispielsweise einen Stapel wie folgt:


Vermeiden Sie dies:


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

Beachten Sie, dass wir die forEach() -Methode aufrufen, die von java.util.Vector geerbt wird. Diese Methode durchläuft den Stapel wie jeder andere Vektor, und genau das brauchen wir. Aber jetzt haben wir uns entschieden, ArrayDeque anstelle von Stack . Wenn wir dies tun, erhält die forEach() -Methode eine Implementierung von ArrayDeque, die den Stapel als Standardstapel (LIFO) durchläuft.


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

Das wollen wir nicht. Es ist zu schwierig, den Fehler hier zu verfolgen, da sich der Code, der den forEach() enthält, nicht neben dem Code befindet, in dem die Änderungen vorgenommen wurden. Um die Geschwindigkeit beim Suchen und Beheben von Fehlern zu erhöhen, ist es viel besser, Code mit der stack so nah wie möglich an der Deklaration dieser Variablen zu schreiben.


Dies geschieht am besten wie folgt:


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

Wenn der Entwickler jetzt von Stack zu ArrayQueue , kann er den Fehler schnell erkennen und beheben.


Klausel 12: Der var-Typ vereinfacht die Verwendung verschiedener Typen in ternären Operatoren


Wir können verschiedene Arten von Operanden auf der rechten Seite des ternären Operators verwenden.


Bei der expliziten Angabe von Typen wird der folgende Code nicht kompiliert:


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

Trotzdem können wir das tun:


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

Der folgende Code wird auch nicht kompiliert:


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

Sie können jedoch allgemeinere Typen verwenden:


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

In all diesen Fällen ist es besser, var zu bevorzugen:


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

Aus diesen Beispielen folgt nicht, dass der var- Typ zur Laufzeit Objekttypen definiert. Es ist nicht so!


Und natürlich funktioniert der var- Typ mit denselben Typen beider Operanden korrekt:


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

Punkt 13: Der Var-Typ kann in Schleifen verwendet werden


Wir können die explizite Deklaration von Typen in for- Schleifen leicht durch den Typ var ersetzen.


Ändern eines expliziten int- Typs in 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 ... } 

Ändern des expliziten Order in var :


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

Punkt 14: var funktioniert gut mit Streams in Java 8


Es ist sehr einfach, var aus Java 10 mit Streams zu verwenden, die in Java 8 angezeigt wurden.


Sie können die explizite Deklaration vom Typ Stream einfach durch var ersetzen:


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

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

Klausel 15: var kann verwendet werden, wenn lokale Variablen deklariert werden, die große Ausdrucksketten in Teile zerlegen sollen


Ausdrücke mit viel Verschachtelung sehen beeindruckend aus und wirken normalerweise wie intelligente und wichtige Codeteile. In dem Fall, dass die Lesbarkeit des Codes verbessert werden muss, wird empfohlen, einen großen Ausdruck mithilfe lokaler Variablen aufzubrechen. Aber manchmal scheint das Schreiben vieler lokaler Variablen ein sehr anstrengender Job zu sein, den ich vermeiden möchte.


Ein Beispiel für einen großen Ausdruck:


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

Teilen Sie den Code besser in seine Bestandteile auf:


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

Die zweite Version des Codes sieht lesbarer und einfacher aus, aber die erste Version hat auch ein Existenzrecht. Es ist absolut normal, dass sich unser Geist an das Verständnis derart großer Ausdrücke anpasst und sie lokalen Variablen vorzieht. Die Verwendung des var- Typs kann jedoch dazu beitragen, große Strukturen aufzubrechen, indem der Aufwand für die Deklaration lokaler Variablen verringert wird:


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

Klausel 16: var kann nicht als Rückgabetyp oder als Methodenargumenttyp verwendet werden


Die beiden unten gezeigten Codefragmente werden nicht kompiliert.


Verwenden von var als Rückgabetyp:


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

Verwenden von var als Art von Methodenargument:


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

Klausel 17: Lokale Variablen vom Typ var können als Parameter der Methode übergeben werden oder den von der Methode zurückgegebenen Wert annehmen


Die folgenden Codefragmente werden kompiliert und funktionieren ordnungsgemäß:


 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; 

Fazit


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


var Java!

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


All Articles