在Java中使用var类型的26条建议


使用JEP 286:Local-Variable Type Inference 将Java局部变量类型推断(LVTI)或简称var类型( var标识符不是关键字,而是保留的类型名称)添加到Java 10中。 作为100%的编译器函数,它不会影响字节码,运行时或性能。 基本上,编译器检查赋值运算符的右侧,并基于该赋值运算符确定变量的特定类型,然后将其替换为var


此外,它对于减少样板代码的冗长性很有用,并且还可以加快编程过程本身。 例如,写var evenAndOdd =...而不是Map<Boolean, List<Integer>> evenAndOdd =...非常方便var evenAndOdd =...


var的出现并不意味着在任何地方使用它总是总是方便的,有时使用标准工具会更实用。


在本文中,我们将研究26种情况,并举例说明何时可以使用var ,以及何时不值得使用var


要点1:尝试给局部变量起有意义的名字


通常,我们专注于为类的字段提供正确的名称,但是我们对局部变量的名称没有给予同样的关注。 当我们的方法实现完美,包含很少的代码并具有良好的名称时,那么我们常常不注意局部变量,甚至完全不使用它们的名称。


当我们使用var而不是编写显式类型时,编译器会自动检测到它们并替换var 。 但是,另一方面,由于使用var会使代码的可读性和理解复杂化,因此人们阅读和理解代码变得更加困难。 在大多数情况下,这是因为我们倾向于将变量的类型视为主要信息,而将其名称视为次要信息。 虽然应该恰恰相反。


范例1:


许多人可能会同意,在下面的示例中,局部变量的名称太短了:


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

当使用短名称以及var时 ,代码变得更加不清楚:


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

首选选项:


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

范例2:


避免这样命名变量:


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

使用更有意义的名称:


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

范例3:


为了给局部变量赋予更易理解的名称,请不要极端:


 // AVOID var byteArrayOutputStream = new ByteArrayOutputStream(); 

相反,您可以使用更简短但也不难理解的选项:


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

您是否知道Java有一个内部类,名为:
InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState


好吧,用这种类型的变量命名可能很麻烦:)


第2点:使用文字帮助var查明基本类型(int,long,float,double)


在原始类型不使用文字的情况下,我们可能会发现预期类型和隐含类型可能会有所不同。 这是由var变量使用的隐式类型转换引起的。


例如,以下两个代码片段的行为符合预期。 在这里,我们显式声明booleanchar类型:


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

现在我们使用var ,而不是显式声明类型:


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

到目前为止一切顺利。 现在对intlongfloatdouble类型执行相同的操作:


 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 

尽管上面的代码段简单明了,但现在让我们使用var而不是显式指定类型。


避免:


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

所有四个变量都将作为int输出。 要解决此问题,我们需要使用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 

但是,如果我们声明一个十进制数会怎样?


如果希望获得float类型的变量,请避免这种情况:


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

为避免意外,请使用适当的文字:


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

要点3:在某些情况下,var和隐式类型转换可以简化代码支持


例如,假设我们的代码在两个方法之间。 一种方法是获取具有不同产品的购物车,然后计算出最佳价格。 为此,他比较了市场上的各种价格,并以浮点型的形式返回了总价格。 另一种方法只是从卡中扣除此价格。


首先,让我们看一下计算最佳价格的方法:


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

其次,让我们看一下适用于地图的方法:


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

现在,我们将代码作为客户端放在这两个外部服务方法之间。 我们的用户可以选择要购买的商品,然后我们为他们计算最优惠的价格,然后从卡中注销资金:


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

一段时间后,拥有API的公司决定放弃价格的材料表示形式,转而使用小数点(现在使用floatint代替)。 因此,他们修改了API代码,如下所示:


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

事实是我们的代码使用float变量的显式声明作为价格。 以当前形式,我们将在编译时收到错误。 但是,如果我们预见到了这种情况,并使用var而不是float ,那么由于隐式类型转换,我们的代码将继续正常工作:


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

要点4:如果文字不是合适的解决方案,请使用显式强制转换或放弃var


Java中的某些原始类型没有特殊的文字,例如字节类型。 在这种情况下,使用显式类型指定,我们可以毫无问题地创建变量。


使用它代替var


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

但是,为什么在这种情况下优先使用显式类型表示法而不是仅使用var ? 好吧,让我们使用var编写这段代码。 请注意,在两种情况下,编译器都会假定您需要int类型的变量。


避免此错误:


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

这里没有任何文字可以帮助我们,因此我们被迫使用显式的向下类型转换。 就我个人而言,我会避免这种情况,因为我在这里看不到任何优势。


仅当您确实要使用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 

要点5:如果变量名称包含的类型信息不足以理解代码,请避免使用var


使用var的好处是编写更简洁的代码。 例如,在使用构造函数的情况下,我们可以避免重复类名的需要,从而消除了代码冗余。


避免以下情况:


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

改用:


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

对于下面的构造, var也是简化代码而不丢失信息的好方法。


避免:


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

使用以下代码:


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

那么,为什么在给出的示例中我们更愿意使用var ? 因为所有必需的信息都包含在变量的名称中。 但是,如果var与变量名结合使用会降低代码的清晰度,则最好拒绝使用它。


避免:


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

用途:


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

例如,考虑使用java.nio.channels.Selector类。 此类具有静态的open()方法,该方法返回一个新的Selector并将其打开。 但是在这里您可以轻松地认为Selector.open()方法可以返回布尔类型,这取决于打开现有选择器的成功程度,甚至可以返回void 。 在此处使用var会导致信息丢失和代码混乱。


要点6:var类型保证了编译时的安全性


这意味着我们无法编译试图执行不正确分配的应用程序。 例如,下面的代码无法编译:


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

但是这个编译:


 var items = 10; items = 20; 

并且此代码成功编译:


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

一旦编译器定义了var变量的值,就不能分配除此类型以外的任何内容。


要点7:var不能用于实例化特定类型并将其分配给接口类型变量


在Java中,我们使用“使用接口编程”方法。 例如,我们创建ArrayList类的实例,并将其与抽象(接口)相关联:


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

而且我们避免将对象绑定到相同类型的变量:


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

这是最常见且最理想的做法,因为我们可以轻松地用任何其他方式替换接口实现。 为此,只需要声明一个接口类型变量。


我们将无法使用var变量来遵循这个概念,因为 始终为他们显示特定类型。 例如,在下面的代码片段中,编译器会将变量的类型确定为ArrayList<String>


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

有几种防御参数可以解释此行为:


  • var用于局部变量,在大多数情况下,使用接口编程要比使用值或字段返回的方法参数少


  • 局部变量的范围应该很小,因此解决由于切换到另一个实现而引起的问题应该不会很困难


  • var将右侧的代码视为用于确定实际类型的初始化程序。 如果在某个时候更改了初始化程序,则定义的类型也可能更改,从而导致依赖此变量的代码出现问题。



要点8:意外类型的推断的可能性


在没有信息来标识类型的情况下将var与菱形运算符 (<>)结合使用可能会导致意外结果。


在Java 7之前,显式类型推断用于集合:


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

从Java 7开始,引入了diamond运算符 。 在这种情况下,编译器将独立派生必要的类型:


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

下面的代码将输出什么类型?


您应该避免这样的构造:


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

该类型将定义为ArrayList<Object> 。 这是因为未提供正确确定类型所需的信息。 这导致将选择最接近的类型,这可以与正在发生的情况相兼容。 在这种情况下, Object


因此,只有在我们提供确定所需类型的必要信息时,才能使用var 。 可以直接指定类型,也可以将其作为参数传递。


直接指定类型:


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

传递所需类型的参数:


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

第9项:将数组分配给var变量不需要方括号[]


我们都知道如何在Java中声明数组:


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

使用数组时如何使用var ? 在这种情况下,无需在左侧使用括号。


避免以下情况(甚至无法编译):


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

用途:


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

下面使用var的代码也无法编译。 这是因为编译器无法从右侧确定类型:


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

项目10:在同一行上声明多个变量时,不能使用var


如果您想一次声明一种类型的变量,那么您需要知道var不适合于此。 以下代码无法编译:


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

改用:


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

还是:


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

要点11:局部变量应努力使其范围最小。 var类型加强了该语句。


对于局部变量,请保留较小的范围-我确定您在var之前听过此声明。


可读性和快速修复错误是支持此方法的理由。 例如,让我们如下定义一个堆栈:


避免这种情况:


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

请注意,我们正在调用forEach()方法,该方法继承自java.util.Vector 。 此方法将像其他任何向量一样遍历堆栈,这就是我们需要的。 但是现在我们决定使用ArrayDeque而不是Stack 。 当我们这样做时, forEach()方法将从ArrayDeque接收实现,该实现将作为标准堆栈(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(...); ... 

这不是我们想要的。 在这里很难跟踪错误,因为包含forEach()部分的代码并不位于进行更改的代码旁边。 为了提高查找和修复错误的速度,最好使用stack变量编写代码,并尽可能靠近此变量的声明。


最佳做法如下:


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

现在,当开发人员从Stack切换到ArrayQueue ,他将能够迅速注意到错误并进行修复。


第12条:var类型简化了三元运算符中各种类型的使用


我们可以在三元运算符的右侧使用不同类型的操作数。


当明确指定类型时,以下代码不会编译:


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

不过,我们可以这样做:


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

以下代码也无法编译:


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

但是您可以使用更多常规类型:


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

在所有这些情况下,最好使用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"; 

从这些示例中不能得出var类型在运行时定义对象类型的结论。 事实并非如此!


而且,当然, var类型将与两个操作数的相同类型一起正常工作:


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

要点13:var类型可以在循环内使用


我们可以轻松地将for循环中类型的显式声明替换 var类型。


将显式int类型更改为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 ... } 

Order的显式类型更改为var


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

要点14:var在Java 8中可以很好地处理流


将Java 10中的var与Java 8中出现的流一起使用非常简单。


您可以简单地用var替换Stream类型的显式声明:


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

范例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> 

第15条:在声明旨在将大型表达式链分解成多个部分的局部变量时,可以使用var


带有大量嵌套的表达式看起来令人印象深刻,通常看起来像是某种智能且重要的代码段。 在需要提高代码可读性的情况下,建议使用局部变量将大表达式分解。 但是有时候写很多局部变量似乎是一项非常累人的工作,我想避免。


一个大型表达式的示例:


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

最好将代码分解为各个组成部分:


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

该代码的第二个版本看起来更易读,更简单,但是第一个版本也有权使用。 对我们来说,适应如此大的表达式并偏向于局部变量更喜欢我们。 但是,使用var类型可以通过减少声明局部变量的工作量来帮助破坏大型结构:


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

第16条:var不能用作返回类型或方法参数类型


下面显示的两个代码段将无法编译。


使用var作为返回类型:


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

使用var作为方法参数的类型:


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

第17条:var类型的局部变量可以作为方法的参数传递,也可以采用方法返回的值


以下代码片段将编译并正常运行:


 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; 

结论


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


var Java!

Source: https://habr.com/ru/post/zh-CN438206/


All Articles