Java局部变量类型推断(LVTI),简称var类型(标识符var不是一个关键字,是一个预留类型名),Java10中新添加的功能。做为100%编译特征,它不会影响字节码,运行时或者性能。在编译时,编译器会检查赋值语句右侧代码,从而推断出具体类型。它查看声明的右侧,若是这是一个初始化语句,它会用那个类型取代var。另外,它很是有助于减小冗余代码和样板代码。html
一般咱们在起全局变量名的时候会注意这一点,可是选择局部变量名的时候不太注意。尤为是当方法很短,方法名和实现都不错的时候,咱们趋向于简化咱们的变量名。可是当咱们使用var替代显式类型的时候,具体的类型是经过编译器推断出来的。因此,对于人来讲阅读或者理解代码很是困难。在这一点上var削弱了代码可读性。这种事情之因此会发生,是由于大多数状况下,咱们会把变量类型当成是第一信息,而把变量名当成第二信息。可是使用var的时候,偏偏相反。java
即便到这里,一些朋友仍然坚持局部变量名短点好。咱们看一下:git
// HAVING
public boolean callDocumentationTask() {
DocumentationTool dtl = ToolProvider.getSystemDocumentationTool();
DocumentationTask dtt = dtl.getTask(...);
return dtt.call();
}
复制代码
咱们换成var时,避免:程序员
public boolean callDocumentationTask() {
var dtl = ToolProvider.getSystemDocumentationTool();
var dtt = dtl.getTask(...);
return dtt.call();
}
复制代码
更好:编程
public boolean callDocumentationTask() {
var documentationTool = ToolProvider.getSystemDocumentationTool();
var documentationTask = documentationTool.getTask(...);
return documentationTask.call();
}
复制代码
避免:数组
public List<Product> fetchProducts(long userId) {
var u = userRepository.findById(userId);
var p = u.getCart();
return p;
}
复制代码
更好:安全
public List<Product> fetchProducts(long userId) {
var user = userRepository.findById(userId);
var productList = user.getCart();
return productList;
}
复制代码
争取为局部变量起有意义的名字并不意味着要掉入过分命名的坑,避免在短方法中使用单一类型的数据流:微信
var byteArrayOutputStream = new ByteArrayOutputStream();
复制代码
用以下代码代替更加清晰:ide
var outputStream = new ByteArrayOutputStream();
// or
var outputStreamOfFoo = new ByteArrayOutputStream();
复制代码
若是在基本数据类型中不使用有效的数据类型标志,咱们可能会发现预期的类型和推测出的类型不一致。这是因为var的隐式类型转换致使的。性能
例如,下面两行代码的表现是符合预期的,首先,咱们声明一个boolean 和一个char使用显式类型:
boolean flag = true; // 这是一个boolean类型
char a = 'a'; // 这是一个char类型
复制代码
如今,咱们使用var代替显式基本类型:
var flag = true; // 被推断为boolean类型
var a = 'a'; // 被推断为char类型
复制代码
到目前为止,一切都很完美。接下来,咱们看一下相同逻辑下的int, long, double 和 float:
int intNumber = 20; // 这是int类型
long longNumber = 20; // 这是long类型
float floatNumber = 20; // 这是float类型, 20.0
double doubleNumber = 20; // 这是double类型, 20.0
复制代码
以上代码是很常见并且清晰的,如今咱们使用var:
避免:
var intNumber = 20; // 推断为int
var longNumber = 20; // 推断为int
var floatNumber = 20; // 推断为int
var doubleNumber = 20; // 推断为int
复制代码
更好:
var intNumber = 20; // 推断为int
var longNumber = 20L; // 推断为long
var floatNumber = 20F; // 推断为float, 20.0
var doubleNumber = 20D; // 推断为double, 20.0
复制代码
可是若是咱们使用小数声明一个数字,会发生什么呢?当你认为你的数字是一个float的时候,避免这样作:
// 避免,若是这是一个float
var floatNumber = 20.5; // 推断为double
复制代码
你应该用对应的数据类型标志来避免这样的问题:
// 更好, 若是这是一个float
var floatNumber = 20.5F; // 推断为float
复制代码
在某些状况下,Var和隐式类型转换能够维持可维护性。例如,假设咱们的代码包含两个方法:第一个方法接收一个包含不一样条目的购物卡,比较市场中不一样的价格,计算出最好的价格,并汇总返回float类型的总价。另外一个方法简单的把这个float价格从卡中扣除。
首先,咱们看一下计算最好价格的方法:
public float computeBestPrice(String[] items) {
...
float price = ...;
return price;
}
复制代码
而后,咱们看一下扣款的方法:
public boolean debitCard(float amount, ...) {
...
}
复制代码
如今,咱们把这两个方法汇总,提供一个服务方法。顾客选择要买的商品,计算最优价格,而后扣款:
//避免
public void purchaseCart(long customerId) {
...
float price = computeBestPrice(...);
debitCard(price, ...);
}
复制代码
一段时间后,公司想要去除价格中的小数部分做为打折策略,使用int代替了float, 咱们须要修改代码:
public int computeBestPrice(String[] items) {
...
float realprice = ...;
...
int price = (int) realprice;
return price;
}
public boolean debitCard(int amount, ...) {
...
}
复制代码
问题在于咱们使用了显示类型float,这样的更改不能被兼容。代码会报编译时错误。可是若是咱们预判到这种状况,使用var代替float, 咱们的代码会由于隐式类型转换而变得没有兼容性问题。
//更好
public void purchaseCart(long customerId) {
...
var price = computeBestPrice(...);
debitCard(price, ...);
}
复制代码
一些Java基础数据类型不支持数据类型标志。例如byte和short。使用显式基础数据类型时没有任何问题。使用var代替的时候:
// 这样更好,而不是使用var
byte byteNumber = 45; // 这是byte类型
short shortNumber = 4533; // 这是short类型
复制代码
为何在这种状况下显式类型比var好呢?咱们切换到var.注意示例中都会被推断为int, 而不是咱们预期的类型。
避免使用如下代码:
var byteNumber = 45; // 推断为int
var shortNumber = 4533; // 推断为int
复制代码
这里没有基础数据类型帮助咱们,所以咱们须要依赖显示强制类型转换。从我的角度来说,我会避免这么用,由于没啥好处,可是能够这么用。
若是你真的想用var,这么用:
var byteNumber = (byte) 45; // 推断为byte
var shortNumber = (short) 4533; // 推断为short
复制代码
使用var有助于提供更加简练的代码。例如, 在使用构造方法时(这是使用局部变量的常见示例),咱们能够简单地避免重复类名的必要性,从而消除冗余。
避免:
MemoryCacheImageInputStream inputStream = new MemoryCacheImageInputStream(...);
复制代码
更好:
var inputStream = new MemoryCacheImageInputStream(...);
复制代码
在下面的结构中,var也是一个简化代码而不丢失信息的好方法。
避免:
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fm = compiler.getStandardFileManager(...);
复制代码
更好:
var compiler = ToolProvider.getSystemJavaCompiler();
var fileManager = compiler.getStandardFileManager(...);
复制代码
为何这样基于var的例子咱们感受比较舒服呢?由于须要的信息已经在变量名中了。可是若是使用var 加上变量名,仍是会丢失信息,那么最好避免使用var。
避免:
public File fetchCartContent() {
return new File(...);
}
// As a human, is hard to infer the "cart" type without
// inspecting the fetchCartContent() method
var cart = fetchCartContent();
复制代码
使用如下代码代替:
public File fetchCartContent() {
return new File(...);
}
File cart = fetchCartContent();
复制代码
思考一个基于java.nio.channels.Selector的例子。这个类有一个静态方法叫作open(),返回一个新的Selector实例而且执行open动做。可是Selector.open()很容易被认为返回一个boolean标识打开当前选择器是否成功,或者返回void。使用var致使丢失信息会引起这样的困扰。
var类型是编译时安全的。这意味着若是咱们试图实现一个错的赋值,会致使编译时报错。例如,如下代码编译不会经过。
// 编译通不过
var items = 10;
items = "10 items"; // 不兼容类型: String不能转为int
复制代码
如下代码编译会经过:
var items = 10;
items = 20;
复制代码
这个代码也会编译经过:
var items = "10";
items = "10 items" ;
复制代码
因此,一旦编译器已经推断出了var对应的类型,咱们只能赋值对应类型的值给它。
在Java中,咱们使用“面向接口编程”的技术。
例如,咱们建立一个ArrayList的实例,以下(绑定代码到抽象):
List<String> products = new ArrayList<>();
复制代码
咱们避免这样的事情(绑定代码到实现):
ArrayList<String> products = new ArrayList<>();
复制代码
因此,经过第一个例子建立一个ArrayList实例更好,可是咱们也须要声明一个List类型的变量。由于List是一个接口,咱们能够很容易的切换到List的其余实现类,而无需额外的修改。
这就是“面向接口编程”,可是var不能这么用。这意味着当咱们使用var时,推断出的类型是实现类的类型。例如,下面这行代码,推测出的类型是ArrayList:
var productList = new ArrayList<String>(); // 推断为ArrayList<String>
复制代码
如下几个论点支持这一行为:
若是不存在推断类型所需的信息,则与菱形运算符组合的var类型可能致使意外推断类型。
在Java 7以前的Coin项目中,咱们写了这样的代码:
//显式指定泛型类的实例化参数类型
List<String> products = new ArrayList<String>();
复制代码
从Java 7开始,咱们有了菱形运算符,它可以推断泛型类实例化参数类型:
// inferring generic class's instantiation parameter type
List<String> products = new ArrayList<>();
复制代码
那么,如下代码推断出什么类型呢?
首先应该避免这么用:
var productList = new ArrayList<>(); // 推断为ArrayList<Object>
复制代码
推断出的类型是Object的ArrayList。之因此会这样是由于没有找到可以推测到预期类型为String的信息,这会致使返回一个最普遍可用类型,Object。
因此为了不这样的情形,咱们必须提供可以推断到预测类型的信息。这个能够直接给也能够间接给。
更好的实现(直接):
var productList = new ArrayList<String>(); // 推断为ArrayList<String>
复制代码
更好的实现(间接):
var productStack = new ArrayDeque<String>();
var productList = new ArrayList<>(productStack); // 推断为ArrayList<String>
复制代码
更好的实现(间接):
Product p1 = new Product();
Product p2 = new Product();
var listOfProduct = List.of(p1, p2); // 推断为List<Product>
// 不要这么干
var listofProduct = List.of(); // 推断为List<Object>
listofProduct.add(p1); //报错
listofProduct.add(p2); //报错
复制代码
咱们都知道Java中如何声明一个数组:
int[] numbers = new int[5];
// 或者,这样写不太好
int numbers[] = new int[5];
复制代码
那么怎么用var呢?左边不须要使用括号。
避免这么写(编译不经过):
// 编译通不过
var[] numbers = new int[5];
// 或者
var numbers[] = new int[5];
复制代码
应该这么用:
var numbers = new int[5]; // 推断为int数组
numbers[0] = 2; // 对
numbers[0] = 2.2; // 错
numbers[0] = "2"; // 错
复制代码
另外,这么用也不能编译,这是由于右边没有本身的类型:
// 显式类型表现符合预期
int[] numbers = {1, 2, 3};
// 编译通不过
var numbers = {1, 2, 3};
var numbers[] = {1, 2, 3};
var[] numbers = {1, 2, 3};
复制代码
若是你喜欢复合声明,你必定要知道var不支持这种声明。下面的代码不能编译:
// 编译通不过
// error: 'var' 不容许复合声明
var hello = "hello", bye = "bye", welcome = "welcome";
复制代码
用下面的代码代替:
String hello = "hello", bye = "bye", welcome = "welcome";
复制代码
或者这么用:
var hello = "hello";
var bye = "bye";
var welcome = "welcome";
复制代码
局部变量应该保持小做用域,我肯定你在var出现以前就听过这个,这样能够加强代码可读性,也方便更快的修复bug。
例如咱们定义一个java栈:
避免:
...
var stack = new Stack<String>();
stack.push("George");
stack.push("Tyllen");
stack.push("Martin");
stack.push("Kelly");
...
// 50行不用stack的代码
// George, Tyllen, Martin, Kelly
stack.forEach(...);
...
复制代码
这不是咱们想要的,咱们很难看出引入了一个错误,由于包含forEach()部分的代码不在研发完成修改的代码附近。为了快速修复这个错误,并避免上下滚动来了解发生了什么,最好缩小stack变量的做用域范围。
最好这么写:
...
var stack = new Stack<String>();
stack.push("George");
stack.push("Tyllen");
stack.push("Martin");
stack.push("Kelly");
...
// George, Tyllen, Martin, Kelly
stack.forEach(...);
...
// 50行不用stack的代码
复制代码
如今,当开发人员从Stack切换到ArrayQueue的时候,他们可以很快的注意到bug,并修复它。
咱们能够在三元运算符的右侧使用不一样类型的操做数。
使用具体类型的时候,如下代码没法编译:
// 编译通不过
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);
复制代码
这样也编译不过:
// 编译通不过:
int code = intOrString ? 12112 : "12112";
String code = intOrString ? 12112 : "12112";
复制代码
可是咱们能够这么写:
Serializable code = intOrString ? 12112 : "12112";
Object code = intOrString ? 12112 : "12112";
复制代码
在这种状况下,使用var更好:
// 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是支持的。
// 推断为float
var code = oneOrTwoDigits ? 1211.2f : 1211.25f;
复制代码
咱们能很是简单的在for循环中用var类型取代具体类型。这是两个例子。
var替换int:
// 显式类型
for (int i = 0; i < 5; i++) {
...
}
// 使用 var
for (var i = 0; i < 5; i++) { // i 推断为 int类型
...
}
复制代码
var替换Order:
List<Order> orderList = ...;
// 显式类型
for (Order order : orderList) {
...
}
// 使用 var
for (var order : orderList) { // order 推断成Order类型
...
}
复制代码
将Java10中的var与Java 8中的Stream结合起来很是简单。
你须要使用var取代显式类型Stream:
例1:
// 显式类型
Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5);
numbers.filter(t -> t % 2 == 0).forEach(System.out::println);
// 使用 var
var numbers = Stream.of(1, 2, 3, 4, 5); // 推断为 Stream<Integer>
numbers.filter(t -> t % 2 == 0).forEach(System.out::println);
复制代码
例2:
// 显式类型
Stream<String> paths = Files.lines(Path.of("..."));
List<File> files = paths.map(p -> new File(p)).collect(toList());
// 使用 var
var paths = Files.lines(Path.of("...")); // 推断为 Stream<String>
var files = paths.map(p -> new File(p)).collect(toList()); // 推断为 List<File>
复制代码
大的或者嵌套的表达看起来使人印象深入,一般它们被认为是聪明的代码。有时候咱们会故意这么写,有时候咱们从一个小表达式开始写,慢慢愈来愈大。为了提升代码可读性,建议用局部变量来破坏大型/嵌套表达式。但有时候,添加这些局部变量是咱们想要避免的体力活。以下:
避免:
List<Integer> intList = List.of(1, 1, 2, 3, 4, 4, 6, 2, 1, 5, 4, 5);
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类型对于使用局部变量的方式来讲是一个优化,由于它节省了获取显式类型的时间。
更好:
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做为方法返回类型:
// 编译通不过
public var countItems(Order order, long timestamp) {
...
}
复制代码
使用var做为方法参数类型:
// 编译通不过
public int countItems(var order, var timestamp) {
...
}
复制代码
下面这两段代码可以编译并且运行:
public int countItems(Order order, long timestamp) {
...
}
public boolean checkOrder() {
var order = ...; // Order实例
var timestamp = ...; // long类型的 timestamp
var itemsNr = countItems(order, timestamp); // 推断为int类型
...
}
复制代码
它也适用于泛型。下面的代码片断也是对的:
public <A, B> B contains(A container, B tocontain) {
...
}
var order = ...; // Order实例
var product = ...; // Product实例
var resultProduct = contains(order, product); // inferred as Product type
复制代码
避免:
public interface Weighter {
int getWeight(Product product);
}
Weighter weighter = new Weighter() {
@Override
public int getWeight(Product product) {
...
}
};
Product product = ...; // Product实例
int weight = weighter.getWeight(product);
复制代码
更好:
public interface Weighter {
int getWeight(Product product);
}
var weighter = new Weighter() {
@Override
public int getWeight(Product product) {
...
}
};
var product = ...; // Product实例
var weight = weighter.getWeight(product);
复制代码
从Java SE 8开始,局部类能够访问封闭块内final或者effectively final的参数。变量初始化后再也不改变的参数为effectively final。
因此,var类型的变量能够是effectively final的。咱们能够从如下代码中看到。
避免:
public interface Weighter {
int getWeight(Product product);
}
int ratio = 5; // 这是effectively final
Weighter weighter = new Weighter() {
@Override
public int getWeight(Product product) {
return ratio * ...;
}
};
ratio = 3; // 这行赋值语句会报错
复制代码
更好:
public interface Weighter {
int getWeight(Product product);
}
// PREFER
var ratio = 5; // 这是effectively final
var weighter = new Weighter() {
@Override
public int getWeight(Product product) {
return ratio * ...;
}
};
ratio = 3; // 这行赋值语句会报错
复制代码
默认状况下,var类型的局部变量能够被从新赋值(除非它是effectively final的)。可是咱们能够声明它为final类型,以下:
避免:
public void discount(int price) {
final int limit = 2000;
final int discount = 5;
if (price > limit) {
discount++; // 这行会报错
}
}
复制代码
更好:
public void discount(int price) {
final var limit = 2000;
final var discount = 5;
if (price > limit) {
discount++; // 这行会报错
}
}
复制代码
当对应的类型推断不出来时不能使用var类型。因此,lambda表达式和方法引用初始化不被容许。这是var类型限制的一部分。
下面的代码没法编译:
// 编译不经过
// lambda表达式须要显式目标类型
var f = x -> x + 1;
// 方法引用须要显式目标类型
var exception = IllegalArgumentException::new;
复制代码
用如下代码代替:
Function<Integer, Integer> f = x -> x + 1;
Supplier<IllegalArgumentException> exception = IllegalArgumentException::new;
复制代码
可是在lambda的内容中,Java 11容许咱们去使用var做为lambda参数。例如,如下代码在Java 11中能够很好的工做(详见JEP 323(lambda参数中的局部变量))
// Java 11
(var x, var y) -> x + y
// or
(@Nonnull var x, @Nonnull var y) -> x + y
复制代码
此外,也不容许缺乏初始化程序。这是var类型的另外一个限制。
如下代码不会编译经过(赋值null):
// 编译通不过
var message = null; // 类型错误: 变量初始化为'null'
复制代码
这个代码也不会编译经过(缺乏初始化):
// IT DOESN'T COMPILE
var message; // 使用var不能不作初始化
...
message = "hello";
复制代码
更好:
String message = null;
// or
String message;
...
message = "hello";
复制代码
var类型能够用来作局部变量,可是不能用来作对象的域/全局变量。
这个限制会致使这里的编译错误:
// 编译通不过
public class Product {
private var price; // 'var' 不被容许
private var name; // 'var' 不被容许
...
}
复制代码
用如下代码代替:
public class Product {
private int price;
private String name;
...
}
复制代码
可是它被容许在try-with-resources中。
当代码抛出异常时,咱们必须经过显式类型catch它,由于var类型不被容许。这个限制会致使如下代码的编译时错误:
// 编译通不过
try {
TimeUnit.NANOSECONDS.sleep(5000);
} catch (var ex) {
...
}
复制代码
用这个取代:
try {
TimeUnit.NANOSECONDS.sleep(5000);
} catch (InterruptedException ex) {
...
}
复制代码
另外一方面,var类型能够用在try-with-resource中,例如:
// 显式类型
try (PrintWriter writer = new PrintWriter(new File("welcome.txt"))) {
writer.println("Welcome message");
}
复制代码
能够用var重写:
// 使用 var
try (var writer = new PrintWriter(new File("welcome.txt"))) {
writer.println("Welcome message");
}
复制代码
假定咱们有下面的代码:
public <T extends Number> T add(T t) {
T temp = t;
...
return temp;
}
复制代码
这种状况下,使用var的运行结果是符合预期的,咱们能够用var替换T,以下:
public <T extends Number> T add(T t) {
var temp = t;
...
return temp;
}
复制代码
咱们看一下另外一个var可以成功使用的例子,以下:
public <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"); // 错误:类型不兼容,string不能转为T
...
}
复制代码
能够用var取代List, 以下:
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"); // // 错误:类型不兼容,string不能转为T
...
}
复制代码
这么作是安全的:
// 显式类型
Class<?> clazz = Integer.class;
// 使用var
var clazz = Integer.class;
复制代码
可是,不要由于代码中有错误,而var可让它们魔法般的消失,就使用var取代Foo。看下一个例子,不是很是明显,可是我想让它指出核心。考虑一下当你编写这一段代码的过程,也许,你尝试定义一个String的ArrayList,并最终定义成了Collection。
// 显式类型
Collection<?> stuff = new ArrayList<>();
stuff.add("hello"); // 编译错误
stuff.add("world"); // 编译错误
// 使用var,错误会消失,可是我不肯定你是你想要的结果
var stuff = new ArrayList<>();
strings.add("hello"); // 错误消失
strings.add("world"); // 错误消失
复制代码
咱们知道能够这么写:
// 显式类型
Class<? extends Number> intNumber = Integer.class;
Class<? super FilterReader> fileReader = Reader.class;
复制代码
并且若是咱们错误赋值了错误的类型,接收到一个编译时错误,这就是咱们想要的:
// 编译通不过
// 错误: Class<Reader> 不能转换到 Class<? extends Number>
Class<? extends Number> intNumber = Reader.class;
// 错误: Class<Integer> 不能转化到Class<? super FilterReader>
Class<? super FilterReader> fileReader = Integer.class;
复制代码
可是若是咱们使用var:
// using var
var intNumber = Integer.class;
var fileReader = Reader.class;
复制代码
而后咱们能够为这些变量赋值任何类,所以咱们的边界/约束消失了,这并非咱们想要的:
// 编译经过
var intNumber = Reader.class;
var fileReader = Integer.class;
复制代码
本篇大篇幅复制于:www.cnblogs.com/shenpengyan…
欢迎扫码或微信搜索公众号《程序员果果》关注我,关注有惊喜~