26 rekomendasi untuk menggunakan tipe var di Jawa


Java Local Variable Type Inference (LVTI) atau, singkatnya, tipe var (pengidentifikasi var bukan kata kunci, tetapi nama tipe yang dicadangkan) ditambahkan ke Java 10 menggunakan JEP 286: Inferensi Tipe Variabel Lokal . Menjadi fungsi kompilator 100%, itu tidak mempengaruhi bytecode, runtime, atau kinerja. Pada dasarnya, kompiler memeriksa sisi kanan operator penugasan dan, berdasarkan hal itu, menentukan jenis variabel tertentu, dan kemudian menggantinya dengan var .


Selain itu, berguna untuk mengurangi verbositas kode boilerplate, dan juga mempercepat proses pemrograman itu sendiri. Sebagai contoh, sangat mudah untuk menulis var evenAndOdd =... daripada Map<Boolean, List<Integer>> evenAndOdd =...


Munculnya var tidak berarti bahwa selalu dan nyaman untuk menggunakannya di mana-mana, kadang-kadang akan lebih praktis untuk dilakukan dengan alat standar.


Pada artikel ini, kita akan melihat 26 situasi, dengan contoh kapan Anda dapat menggunakan var , dan ketika itu tidak sepadan.


Butir 1: coba berikan nama yang bermakna ke variabel lokal


Biasanya kami fokus pada pemberian nama yang benar untuk bidang kelas, tetapi kami tidak memperhatikan nama variabel lokal. Ketika metode kami benar-benar diimplementasikan, mengandung sedikit kode dan memiliki nama baik, maka sangat sering kita tidak memperhatikan variabel lokal, atau bahkan sepenuhnya mengurangi nama mereka.


Ketika kita menggunakan var daripada menulis tipe eksplisit, kompiler secara otomatis mendeteksi mereka dan mengganti var . Tetapi di sisi lain, sebagai akibat dari ini, menjadi lebih sulit bagi orang untuk membaca dan memahami kode, karena menggunakan var dapat memperumit keterbacaan dan pemahamannya. Dalam kebanyakan kasus, ini karena kita cenderung melihat jenis variabel sebagai informasi primer, dan namanya sebagai sekunder. Meski seharusnya justru sebaliknya.


Contoh 1:


Banyak yang mungkin akan setuju bahwa dalam contoh di bawah ini, nama-nama variabel lokal terlalu pendek:


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

Saat menggunakan nama pendek, bersama dengan var , kode menjadi semakin tidak jelas:


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

Opsi yang lebih disukai:


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

Contoh 2:


Hindari penamaan variabel seperti ini:


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

Gunakan nama yang lebih bermakna:


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

Contoh 3:


Dalam upaya memberikan nama yang lebih komprehensif ke variabel lokal, jangan terlalu ekstrem:


 // AVOID var byteArrayOutputStream = new ByteArrayOutputStream(); 

Sebagai gantinya, Anda dapat menggunakan opsi yang lebih pendek, namun tidak kalah dimengerti:


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

Tahukah Anda bahwa Java memiliki kelas dalam bernama:
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaksimalkanButtonWindowNotFocusedState


Nah, penamaan variabel dengan tipe ini bisa rumit :)


Poin 2: gunakan literal untuk membantu var menentukan jenis primitif (int, panjang, float, dobel)


Tanpa menggunakan literal untuk tipe primitif, kita mungkin menemukan bahwa tipe yang diharapkan dan tersirat mungkin berbeda. Ini disebabkan oleh konversi tipe implisit yang digunakan oleh variabel var .


Misalnya, dua fragmen kode berikut berperilaku seperti yang diharapkan. Di sini kami secara eksplisit mendeklarasikan tipe boolean dan char :


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

Sekarang kita menggunakan var , bukan tipe yang secara eksplisit menyatakan:


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

Sejauh ini bagus. Sekarang lakukan hal yang sama untuk tipe int , long , float, dan 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 

Meskipun potongan kode di atas sederhana dan mudah, sekarang mari kita gunakan var bukan tipe yang secara eksplisit menentukan.


Hindari:


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

Keempat variabel akan menjadi output sebagai int . Untuk memperbaiki perilaku ini, kita perlu menggunakan literal 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 

Tetapi apa yang terjadi jika kita mendeklarasikan angka desimal?


Hindari ini jika Anda berharap mendapatkan variabel tipe float :


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

Untuk menghindari kejutan, gunakan literal yang sesuai:


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

Butir 3: dalam beberapa kasus konversi tipe var dan implisit dapat menyederhanakan dukungan kode


Sebagai contoh, mari kita asumsikan bahwa kode kita ada di antara dua metode. Salah satu metode mendapatkan keranjang belanja dengan produk yang berbeda dan menghitung harga terbaik. Untuk melakukan ini, ia membandingkan berbagai harga di pasar dan mengembalikan harga total dalam bentuk tipe float . Metode lain hanya mengurangi harga ini dari kartu.


Pertama, mari kita lihat metode yang menghitung harga terbaik:


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

Kedua, mari kita lihat metode yang bekerja dengan peta:


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

Sekarang kami menempatkan kode kami di antara dua metode layanan eksternal ini sebagai klien. Pengguna kami dapat memilih barang untuk dibeli, dan kami menghitung harga terbaik untuk mereka, dan kemudian menghapus dana dari kartu:


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

Setelah beberapa waktu, perusahaan yang memiliki API memutuskan untuk meninggalkan representasi material dari harga yang mendukung desimal (bukan float , int sekarang digunakan). Jadi, mereka memodifikasi kode API sebagai berikut:


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

Faktanya adalah bahwa kode kami menggunakan deklarasi eksplisit dari variabel float sebagai harga. Dalam bentuk saat ini, kami akan menerima kesalahan pada waktu kompilasi. Tetapi jika kita telah meramalkan situasi seperti itu dan menggunakan var alih-alih float , maka kode kita akan terus bekerja tanpa masalah, berkat konversi tipe implisit:


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

Butir 4: ketika literal bukan solusi yang cocok, gunakan casting eksplisit atau buang var


Beberapa tipe primitif di Jawa tidak memiliki literal khusus, misalnya tipe byte dan pendek . Dalam hal ini, menggunakan penunjukan tipe eksplisit, kita dapat membuat variabel tanpa masalah.


Gunakan ini sebagai ganti var :


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

Tetapi mengapa dalam situasi ini memberikan preferensi untuk notasi jenis eksplisit daripada hanya menggunakan var ? Baiklah, mari kita menulis kode ini menggunakan var . Perhatikan bahwa dalam kedua kasus, kompiler akan menganggap bahwa Anda memerlukan variabel tipe int .


Hindari kesalahan ini:


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

Tidak ada literal di sini yang akan membantu kami, oleh karena itu kami terpaksa menggunakan konversi tipe eksplisit ke bawah. Secara pribadi, saya akan menghindari situasi seperti itu, karena saya tidak melihat keuntungan apa pun di sini.


Gunakan hanya entri ini jika Anda benar-benar ingin menggunakan 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 

Butir 5: hindari menggunakan var jika nama variabel tidak mengandung informasi jenis yang cukup untuk memahami kode


Keuntungan menggunakan var adalah menulis kode yang lebih ringkas. Sebagai contoh, dalam kasus menggunakan konstruktor, kita dapat menghindari kebutuhan untuk mengulangi nama kelas dan, karenanya, menghilangkan redundansi kode.


Hindari yang berikut ini:


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

Gunakan sebaliknya:


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

Untuk konstruksi di bawah ini, var juga akan menjadi cara yang baik untuk menyederhanakan kode tanpa kehilangan informasi.


Hindari:


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

Gunakan kode berikut:


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

Jadi, mengapa kita lebih nyaman bekerja dengan var dalam contoh yang disajikan? Karena semua informasi yang diperlukan terkandung dalam nama-nama variabel. Tetapi jika var , dalam kombinasi dengan nama variabel, mengurangi kejelasan kode, lebih baik menolak untuk menggunakannya.


Hindari:


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

Gunakan:


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

Pertimbangkan, misalnya, penggunaan kelas java.nio.channels.Selector . Kelas ini memiliki metode open() statis yang mengembalikan Pemilih baru dan membukanya. Tetapi di sini Anda dapat dengan mudah berpikir bahwa metode Selector.open() dapat mengembalikan tipe boolean , tergantung pada keberhasilan membuka pemilih yang ada, atau bahkan mengembalikan kekosongan . Menggunakan var di sini akan menyebabkan hilangnya informasi dan kebingungan dalam kode.


Butir 6: tipe var menjamin keamanan waktu kompilasi


Ini berarti bahwa kami tidak dapat mengkompilasi aplikasi yang mencoba melakukan tugas yang salah. Misalnya, kode di bawah ini tidak dapat dikompilasi:


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

Tetapi yang ini mengkompilasi:


 var items = 10; items = 20; 

Dan kode ini berhasil dikompilasi:


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

Setelah kompiler menetapkan nilai variabel var , kami tidak dapat menetapkan apa pun selain tipe ini.


Butir 7: var tidak dapat digunakan untuk instantiate jenis tertentu dan menetapkannya ke variabel jenis antarmuka


Di Jawa, kami menggunakan pendekatan "pemrograman dengan antarmuka". Sebagai contoh, kami membuat turunan dari kelas ArrayList, mengaitkannya dengan abstraksi (antarmuka):


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

Dan kami menghindari hal-hal seperti mengikat objek ke variabel dengan tipe yang sama:


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

Ini adalah praktik yang paling umum dan diinginkan, karena kita dapat dengan mudah mengganti implementasi antarmuka dengan yang lain. Untuk ini, hanya perlu mendeklarasikan variabel tipe antarmuka.


Kami tidak akan dapat mengikuti konsep ini menggunakan variabel var, seperti tipe tertentu selalu ditampilkan untuk mereka. Misalnya, dalam cuplikan kode berikut, kompiler akan menentukan tipe variabel sebagai ArrayList<String> :


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

Ada beberapa argumen pembelaan yang menjelaskan perilaku ini:


  • var digunakan untuk variabel lokal, di mana, dalam kebanyakan kasus, pemrograman menggunakan antarmuka kurang dari dalam kasus dengan parameter metode yang dikembalikan oleh nilai atau bidang


  • Ruang lingkup variabel lokal harus kecil, sehingga menyelesaikan masalah yang disebabkan oleh beralih ke implementasi lain seharusnya tidak terlalu sulit


  • var memperlakukan kode di sebelah kanan sebagai inisialisasi yang digunakan untuk menentukan tipe aktual. Jika, pada beberapa titik, initializer diubah, maka jenis yang didefinisikan juga dapat berubah, menyebabkan masalah dalam kode yang bergantung pada variabel ini.



Paragraf 8: probabilitas kesimpulan dari tipe yang tidak terduga


Menggunakan var dalam kombinasi dengan operator intan (<>) tanpa adanya informasi untuk mengidentifikasi tipe dapat menyebabkan hasil yang tidak terduga.


Sebelum Java 7, inferensi tipe eksplisit digunakan untuk koleksi:


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

Dimulai dengan Java 7, operator berlian diperkenalkan. Dalam hal ini, kompiler akan secara independen mendapatkan tipe yang diperlukan:


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

Jenis apa yang akan dihasilkan dalam kode di bawah ini?


Anda harus menghindari konstruksi tersebut:


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

Jenis akan didefinisikan sebagai ArrayList<Object> . Ini karena informasi yang diperlukan untuk menentukan dengan benar jenis tidak disediakan. Ini mengarah pada fakta bahwa tipe terdekat akan dipilih, yang dapat kompatibel dengan konteks apa yang terjadi. Dalam hal ini, Object .


Dengan demikian, var hanya dapat digunakan jika kami memberikan informasi yang diperlukan untuk menentukan jenis yang diharapkan. Jenisnya dapat ditentukan secara langsung atau diteruskan sebagai argumen.


Tentukan jenisnya secara langsung:


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

Lewati argumen dari jenis yang diperlukan:


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

Butir 9: menugaskan array ke variabel var tidak memerlukan tanda kurung []


Kita semua tahu cara mendeklarasikan array di Jawa:


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

Bagaimana dengan menggunakan var saat bekerja dengan array? Dalam hal ini, tidak perlu menggunakan tanda kurung di sisi kiri.


Hindari yang berikut ini (ini bahkan tidak dapat dikompilasi):


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

Gunakan:


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

Kode di bawah ini menggunakan var juga gagal dikompilasi. Ini karena kompiler tidak dapat menentukan jenis dari sisi kanan:


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

Butir 10: var tidak dapat digunakan saat mendeklarasikan beberapa variabel pada baris yang sama


Jika Anda ingin mendeklarasikan variabel dari tipe yang sama sekaligus, maka Anda perlu tahu bahwa var tidak cocok untuk ini. Kode berikut tidak dikompilasi:


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

Gunakan sebaliknya:


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

Atau apakah itu:


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

Butir 11: variabel lokal harus berusaha meminimalkan ruang lingkupnya. Tipe var memperkuat pernyataan ini.


Simpan cakupan kecil untuk variabel lokal - Saya yakin Anda pernah mendengar pernyataan ini sebelum var .


Keterbacaan dan perbaikan bug cepat adalah argumen yang mendukung pendekatan ini. Misalnya, mari kita tentukan tumpukan sebagai berikut:


Hindari ini:


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

Perhatikan bahwa kita memanggil metode forEach() , yang diwarisi dari java.util.Vector . Metode ini akan melalui tumpukan seperti vektor lainnya dan inilah yang kami butuhkan. Tapi sekarang kami memutuskan untuk menggunakan ArrayDeque daripada Stack . Ketika kita melakukan ini, metode forEach() akan menerima implementasi dari ArrayDeque yang akan melintasi stack sebagai stack standar (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(...); ... 

Ini bukan yang kita inginkan. Terlalu sulit untuk melacak kesalahan di sini, karena kode yang berisi bagian forEach() tidak terletak di sebelah kode tempat perubahan dilakukan. Untuk meningkatkan kecepatan pencarian dan memperbaiki kesalahan, lebih baik menulis kode menggunakan variabel stack , sedekat mungkin dengan deklarasi variabel ini.


Ini paling baik dilakukan sebagai berikut:


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

Sekarang, ketika pengembang beralih dari Stack ke ArrayQueue , ia akan dapat dengan cepat melihat kesalahan dan memperbaikinya.


Klausul 12: tipe var menyederhanakan penggunaan berbagai tipe dalam operator ternary


Kita dapat menggunakan berbagai jenis operan di sisi kanan operator ternary.


Saat menentukan jenis secara eksplisit, kode berikut ini tidak dapat dikompilasi:


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

Namun demikian, kita dapat melakukan ini:


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

Kode di bawah ini juga tidak dapat dikompilasi:


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

Tetapi Anda dapat menggunakan jenis yang lebih umum:


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

Dalam semua kasus seperti itu, lebih baik untuk memilih 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"; 

Ini tidak mengikuti dari contoh-contoh ini bahwa tipe var mendefinisikan tipe objek saat runtime. Ini tidak benar!


Dan, tentu saja, tipe var akan bekerja dengan benar dengan tipe yang sama dari kedua operan:


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

Poin 13: tipe var dapat digunakan di dalam loop


Kita dapat dengan mudah mengganti deklarasi eksplisit tipe dalam untuk loop dengan tipe var .


Mengubah tipe int eksplisit ke 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 ... } 

Mengubah jenis Order menjadi var :


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

Butir 14: var berfungsi baik dengan stream di Java 8


Sangat mudah untuk menggunakan var dari Java 10 dengan stream yang muncul di Java 8.


Anda cukup mengganti deklarasi eksplisit tipe stream dengan var :


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

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

Klausul 15: var dapat digunakan ketika mendeklarasikan variabel lokal yang dimaksudkan untuk memecah rantai ekspresi menjadi beberapa bagian


Ekspresi dengan banyak bersarang terlihat mengesankan dan biasanya tampak seperti semacam potongan kode yang cerdas dan penting. Dalam kasus di mana perlu untuk memfasilitasi keterbacaan kode, disarankan untuk memecah ekspresi besar menggunakan variabel lokal. Tetapi kadang-kadang menulis banyak variabel lokal sepertinya pekerjaan yang sangat melelahkan yang ingin saya hindari.


Contoh ekspresi besar:


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

Lebih baik memecah kode menjadi bagian-bagian komponennya:


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

Versi kode yang kedua terlihat lebih mudah dibaca dan lebih sederhana, tetapi versi pertama juga memiliki hak untuk ada. Sangatlah normal bagi pikiran kita untuk beradaptasi dengan pemahaman ekspresi yang begitu besar dan lebih menyukai variabel lokal. Namun, menggunakan tipe var dapat membantu memecah struktur besar dengan mengurangi upaya untuk mendeklarasikan variabel lokal:


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

Klausa 16: var tidak dapat digunakan sebagai tipe kembali atau sebagai tipe argumen metode


Dua cuplikan kode yang ditunjukkan di bawah ini tidak dapat dikompilasi.


Menggunakan var sebagai tipe pengembalian:


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

Menggunakan var sebagai jenis argumen metode:


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

Klausa 17: variabel lokal dari tipe var dapat diteruskan sebagai parameter metode atau mereka dapat mengambil nilai yang dikembalikan oleh metode


Fragmen kode berikut ini akan dikompilasi dan akan berfungsi dengan baik:


 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; 

Kesimpulan


ยซ var ยป, Java 10. , . , var , .


var Java!

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


All Articles