- 浏览: 102763 次
- 性别:
- 来自: 无锡
文章分类
最新评论
-
solen:
Gmail错误:SMTP服务器需要安全连接或客户端未通过身份验 ...
C#发送邮件三种方法,Localhost,SMTP,SSL-SMTP -
Ccokone:
看了一下源码,好像并不是什么独立线程~LZ你怎么看~
快速失败 -
yeak2001:
傲视温柔 写道请问一下 @Component public ...
@InitBinder -
傲视温柔:
请问一下 @Component public class C ...
@InitBinder -
xubindehao:
写的不错,其实所谓的索引就是每一个itor类目维护一个修改次数 ...
快速失败
在正式开讲之前先容许我说下写这篇文章的故事背景。前几天我们的production下的一个tool突然莫名其妙的报错,那部分功能已经很久没有改动过了,按理说是不应该出现问题的,代码在做反射调用method的时候出现了ClassCastException。我先是以为可能是什么小问题就把任务分给我同事了,他分析下来告诉我不知道什么问题,莫名其妙的就突然抛异常了;那找不到问题我们就只能怪JAVA Compiler了 原来最近我们做了一次JDK的升级,从7升级到了8,起先以为是reflect的Method类有所改动,结果比较以后一模一样 两眼一抹黑,完蛋。。。。 好了,谜底我会在最后揭露。
Knowledge lets you deduce the right thing to do; Expertise makes the right thing a reflex.
《Unix 编程艺术》
程序员最重要的是思想,知其然知其所以然。
下面进入今天的主题 Type Inference.
一, Type Inference
什么是Type Inference,官方给出的定义是:
Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable. The inference algorithm determines the types of the arguments and, if available, the type that the result is being assigned, or returned. Finally, the inference algorithm tries to find the most specific type that works with all of the arguments.
大意就是:
类型推断是一个Java编译器来查看每一个方法调用和相应的声明,以确定类型参数(或参数),使调用能够正常实现。推理算法确定参数类型,如果类型推断成功,那么方法返回的值就是那个类型的。最后,推理算法试图找到与所有的变量工作的最具体类型。
如何理解这段话呢,我们先把这段话拆分成几个概念:
二, Generic Method
要想把Type Inference说清楚了还是要先从Generic Method说起的,什么是Generic Method? 看下面的例子
The Util class includes a generic method, compare, which compares two Pair objects:
这就是一个经典的Generic Method,这个方法叫做 compare方法接受2个类型参数为K和V的Pair类型(这里就不细说泛型了)。Generic Method 有几部分组成:
1. Type Parameter, 尖括号包住的部分
2. 一个相同的<Type Parameter>出现在返回类型前,如果是static method那么这个<Type Parameter>是必须出现的。
3. 一个返回类型,可以是Type Parameter对应的那个类型,也可以不是。
那么到底如何去确定K和V的类型呢,这个类型要在方法调用的时候才能确定下来,这就要来说type inference了.
三, Type Inference 实例解说
假设我有一个Generic Method, T在这里就是type argument,这个方法接受2个T类型的参数,返回一个T类型的结果。
1.
现在去call这个method,
我们来拆分一下:
1. 第一个a1参数传入”d”,类型是String.
2. 第二个a2参数传入ArrayList<String>, 类型是ArrayList.
3. String和ArrayList都是interface Serializable的实现,所以pick method的返回值被infers成Serializable 类型。
2. 再来看一个Generic Method的例子
The following is the output from this example:
Box #0 contains [10]
Box #1 contains [20]
Box #2 contains [30]
addBox是一个Generic Method接受一个U类型的参数,当然这个方法是接受2个参数的,第一个是U类型的参数,第2个是U类型的Box类型的List, 大家可以看到在main方法里我们用了2种方式来调用addBox,第一种是显示的告诉Java Compiler我要用Integer这个类型,第二种就是类型推断,我并没有显示的指定我要用Integer, Java Compile根据传入参数的类型来推断出method应该使用哪种类型.
四, Target Type 目标类型
Java Compiler 会根据你指定的目标类型来推断(infers)出method该返回哪种类型的结果,例如:
我们再考虑一种情况,现在我有一个方法接受一个List<String>的参数。
现在调用:
这个在Java 7里是不会编译的,因为Java 7不支持 method 类型推断,T类型默认就是Object,然后就出现了编译错误
List<Object> cannot be converted to List<String>。
五, Context
上面说到method类型推断,什么是method类型推断,那就要说下这个context的概念了,Java Compiler在做类型推断的时候主要依靠的就是上下文。目前有2种context.
Assignment Context就是赋值上下文,也很好理解了就是依靠赋值语句左边的类型来推断generic method的具体类型。
Method Context顾名思义就是method上下文了,这个概念不像assignment来的那么直接,而且JDK 7没有method infers这个东西。Method上下文就是根据接受参数的method的参数类型来推断被传入调用method的类型。
上面的例子放在JDK 8里就变得有意义了
现在调用:
JDK 8引入了method infers,也就是说Java Compiler会根据当前method的上下文来决定那个T类型到底应该是什么类型,在这里就是String类型。
说到JAVA 8那就不能不说lambda了
六, Lambda & Stream
等等这里不是说Type Inference吗为什么要说Lambda? Lambda很重要的一个核心概念就是类型推断。
先看一个列子:
Predicate<Integer> predicate = (var) -> var > 0; //P.S. 第一眼看上去还是很cool的
要说JAVA的lambda那就要说functional interface, functional interface就是只有一个抽象方法的接口,这样的接口都可以叫做functional interface.
那么这个lambda expression到底和type inference有什么关系呢,首先我们来看一下Predicate接口的方法声明
boolean test(T t);
上面的lambda expression之所以能够成功就是因为这个方法的定义,接受一个T类型的参数,返回一个boolean值,这里面牵涉到一个function descriptor 这里就不细说了,以后有机会单做一期Lambda;再来看上面的赋值语句,单看(var) -> var > 0根本不知道这个var是什么类型的,当这个expression赋值给Perdicate<Integer>的时候按照assignment context的类型推断这个var就是一个Integer的类型。
java.util.function package下的所有预先定义好的functional interface都全部依赖type inference.
下面看一个关于Stream的例子:
filter方法接受一个Perdicate<T>的参数, map 方法接受一个Function<T, R>的参数,collect接受一个类型为Collect的参数,这个Collect是由Collectors这个utility class来构造的,如果你翻看Collectors的源码的话你会发现几乎所有的方法都用到了generic method。 所有的这一切都源之于 method context的类型推断。
如果没有JAVA 8的method context类型推断你根本就无法使用这种chain的结构,也无法写出这么简洁的代码.
七, 遗留的问题
最后来说一下开篇的时候留下的问题,在确定是类型推断问题之前我一度以为JDK 8存在bug,也确实有人遇到了同样的问题并且在OpenJDK里报了bug https://bugs.openjdk.java.net/browse/JDK-8072919, 但问题被resolve了并且说这不是一个bug,好吧我承认这确实不是一个bug。
问题是这样的, 现在有2个方法分别如下:
方法1:
接受2个参数一个Object, 一个 Object[],其实就是来至于reflect的Method.invoke
方法2:
用2个方法调用invoke方法,
1. invoke(“1231”, readValue());
2. List<String> list = readValue(); invoke(“12312”, list);
我们来分析一下2种不同的调用方法:
1. 第一种方法直接把readValue()的返回值当做参数传入invoke方法,这时候就需要用method context来infers readValue的返回类型,invoke method的第二个参数是Object...也就是Object[],那么通过infers就确定了readValue 的类型是Object[], oooooops, 这段代码会抛出一个ClassCastException, 因为 ArrayList can not cast to Object[], java.utils.ArrayList cannot be cast to [Ljava.lang.Object;
2. 第二个方法可以正确执行,因为我们先调用readValue方法然后赋值给list variable,这时候就有了target type, Java Compiler通过infers决定返回List<String>的值, 再把list这个变量传入invoke这个method就没有问题了.
3. 注意:这2种不同的调用方法在JDK 7的时候都能执行成功,因为JDK 7没有method context的类型推断,所以T被当成了Object,那么在readValue内部类型转换就没有问题了,因为所有的类都继承了Object。
为了更直观的看下JVM到底做了什么,我写了一个简单的小例子,然后我们看一下Java Compiler 对class字节码到底做了什么。
e.g. 1:
这是会出现ClassCastException的代码
java.lang.ClassCastException: java.util.ArrayList cannot be cast to [Ljava.lang.Object;
e.g. 2: 这是可以执行的代码:
这2个不同的调用方式在这个字节码里体现的很清楚,第2个调用方法生成了一个类型为list的local variable,并且是一个类型参数为String的list,参考astore_1 iconst_1, aload_1指令。
总结一下: 文章写的好不好,总结很重要
1. Type Inference就是类型推断,根据当前调用method的上下文来推断出具体的类型。
2. 如果method有一个T类型的参数,那么T的类型就由传入参数的类型决定。
3. 如果method没有类型参数,但却有一个T类型的返回,那么就要考虑context,target type,是assignment context还是method context。
4. JDK 8引入了method context的概念来实现method infers type parameters。functional interface以及Stream API大量使用method类型推断。(如果大家有兴趣,我会单独做一期关于Lambda和Stream的文章。
希望我解释的足够清楚能够帮助大家理解透彻Type Inference.
Knowledge lets you deduce the right thing to do; Expertise makes the right thing a reflex.
《Unix 编程艺术》
程序员最重要的是思想,知其然知其所以然。
下面进入今天的主题 Type Inference.
一, Type Inference
什么是Type Inference,官方给出的定义是:
Type inference is a Java compiler's ability to look at each method invocation and corresponding declaration to determine the type argument (or arguments) that make the invocation applicable. The inference algorithm determines the types of the arguments and, if available, the type that the result is being assigned, or returned. Finally, the inference algorithm tries to find the most specific type that works with all of the arguments.
大意就是:
类型推断是一个Java编译器来查看每一个方法调用和相应的声明,以确定类型参数(或参数),使调用能够正常实现。推理算法确定参数类型,如果类型推断成功,那么方法返回的值就是那个类型的。最后,推理算法试图找到与所有的变量工作的最具体类型。
如何理解这段话呢,我们先把这段话拆分成几个概念:
- Generic Method - 泛型方法。
- Type Parameters - 类型参数,也就是Generic Type Parameter
- Method invocation - 方法调用,类型推断主要发生在方法调用的时候。
- Target Type - 目标类型
- Inference algorithm - 类型推断算法,接下来会用实例来说明这个类型推算到底是如何工作的。
二, Generic Method
要想把Type Inference说清楚了还是要先从Generic Method说起的,什么是Generic Method? 看下面的例子
The Util class includes a generic method, compare, which compares two Pair objects:
public class Util { public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) { return p1.getKey().equals(p2.getKey()) && p1.getValue().equals(p2.getValue()); } } public class Pair<K, V> { private K key; private V value; public Pair(K key, V value) { this.key = key; this.value = value; } public void setKey(K key) { this.key = key; } public void setValue(V value) { this.value = value; } public K getKey() { return key; } public V getValue() { return value; } }
这就是一个经典的Generic Method,这个方法叫做 compare方法接受2个类型参数为K和V的Pair类型(这里就不细说泛型了)。Generic Method 有几部分组成:
1. Type Parameter, 尖括号包住的部分
2. 一个相同的<Type Parameter>出现在返回类型前,如果是static method那么这个<Type Parameter>是必须出现的。
3. 一个返回类型,可以是Type Parameter对应的那个类型,也可以不是。
那么到底如何去确定K和V的类型呢,这个类型要在方法调用的时候才能确定下来,这就要来说type inference了.
三, Type Inference 实例解说
假设我有一个Generic Method, T在这里就是type argument,这个方法接受2个T类型的参数,返回一个T类型的结果。
1.
static <T> T pick(T a1, T a2) { return a2; }
现在去call这个method,
Serializable s = pick("d", new ArrayList<String>());
我们来拆分一下:
1. 第一个a1参数传入”d”,类型是String.
2. 第二个a2参数传入ArrayList<String>, 类型是ArrayList.
3. String和ArrayList都是interface Serializable的实现,所以pick method的返回值被infers成Serializable 类型。
2. 再来看一个Generic Method的例子
public class BoxDemo { public static <U> void addBox(U u, java.util.List<Box<U>> boxes) { Box<U> box = new Box<>(); box.set(u); boxes.add(box); } public static <U> void outputBoxes(java.util.List<Box<U>> boxes) { int counter = 0; for (Box<U> box: boxes) { U boxContents = box.get(); System.out.println("Box #" + counter + " contains [" + boxContents.toString() + "]"); counter++; } } public static void main(String[] args) { java.util.ArrayList<Box<Integer>> listOfIntegerBoxes = new java.util.ArrayList<>(); BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes); BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes); BoxDemo.addBox(Integer.valueOf(30), listOfIntegerBoxes); BoxDemo.outputBoxes(listOfIntegerBoxes); } }
The following is the output from this example:
Box #0 contains [10]
Box #1 contains [20]
Box #2 contains [30]
addBox是一个Generic Method接受一个U类型的参数,当然这个方法是接受2个参数的,第一个是U类型的参数,第2个是U类型的Box类型的List, 大家可以看到在main方法里我们用了2种方式来调用addBox,第一种是显示的告诉Java Compiler我要用Integer这个类型,第二种就是类型推断,我并没有显示的指定我要用Integer, Java Compile根据传入参数的类型来推断出method应该使用哪种类型.
四, Target Type 目标类型
Java Compiler 会根据你指定的目标类型来推断(infers)出method该返回哪种类型的结果,例如:
Collections.emptyList static <T> List<T> emptyList(); //这个方法没有参数,只有一个T类型的返回类型,那么我不能传入参数这个方法是如何知道用什么返回类型的呢,这就是target type
List<String> listOne = Collections.emptyList();// listOne是一个List<String>类型的变量,Java Compiler会根据这个目标类型来推断出emptyList method应该返回这个类型,这种类型推断依赖于assignment context,也就是说我要赋给哪个变量,它是什么类型我就返回什么类型。
我们再考虑一种情况,现在我有一个方法接受一个List<String>的参数。
void processStringList(List<String> stringList) { // process stringList }
现在调用:
processStringList(Collections.emptyList());
这个在Java 7里是不会编译的,因为Java 7不支持 method 类型推断,T类型默认就是Object,然后就出现了编译错误
List<Object> cannot be converted to List<String>。
五, Context
上面说到method类型推断,什么是method类型推断,那就要说下这个context的概念了,Java Compiler在做类型推断的时候主要依靠的就是上下文。目前有2种context.
- Assignment Context
- Method Context
Assignment Context就是赋值上下文,也很好理解了就是依靠赋值语句左边的类型来推断generic method的具体类型。
Method Context顾名思义就是method上下文了,这个概念不像assignment来的那么直接,而且JDK 7没有method infers这个东西。Method上下文就是根据接受参数的method的参数类型来推断被传入调用method的类型。
上面的例子放在JDK 8里就变得有意义了
void processStringList(List<String> stringList) { // process stringList }
现在调用:
processStringList(Collections.emptyList());
JDK 8引入了method infers,也就是说Java Compiler会根据当前method的上下文来决定那个T类型到底应该是什么类型,在这里就是String类型。
说到JAVA 8那就不能不说lambda了
六, Lambda & Stream
等等这里不是说Type Inference吗为什么要说Lambda? Lambda很重要的一个核心概念就是类型推断。
先看一个列子:
Predicate<Integer> predicate = (var) -> var > 0; //P.S. 第一眼看上去还是很cool的
要说JAVA的lambda那就要说functional interface, functional interface就是只有一个抽象方法的接口,这样的接口都可以叫做functional interface.
那么这个lambda expression到底和type inference有什么关系呢,首先我们来看一下Predicate接口的方法声明
boolean test(T t);
上面的lambda expression之所以能够成功就是因为这个方法的定义,接受一个T类型的参数,返回一个boolean值,这里面牵涉到一个function descriptor 这里就不细说了,以后有机会单做一期Lambda;再来看上面的赋值语句,单看(var) -> var > 0根本不知道这个var是什么类型的,当这个expression赋值给Perdicate<Integer>的时候按照assignment context的类型推断这个var就是一个Integer的类型。
java.util.function package下的所有预先定义好的functional interface都全部依赖type inference.
下面看一个关于Stream的例子:
List<String> threeHighCaloricDishNames = menu.stream() .filter(d -> d.getCalories() > 300) .map(Dish::getName) .limit(3) .collect(Collectors.toList());
filter方法接受一个Perdicate<T>的参数, map 方法接受一个Function<T, R>的参数,collect接受一个类型为Collect的参数,这个Collect是由Collectors这个utility class来构造的,如果你翻看Collectors的源码的话你会发现几乎所有的方法都用到了generic method。 所有的这一切都源之于 method context的类型推断。
如果没有JAVA 8的method context类型推断你根本就无法使用这种chain的结构,也无法写出这么简洁的代码.
七, 遗留的问题
最后来说一下开篇的时候留下的问题,在确定是类型推断问题之前我一度以为JDK 8存在bug,也确实有人遇到了同样的问题并且在OpenJDK里报了bug https://bugs.openjdk.java.net/browse/JDK-8072919, 但问题被resolve了并且说这不是一个bug,好吧我承认这确实不是一个bug。
问题是这样的, 现在有2个方法分别如下:
方法1:
void invoke(Object obj, Object… objs)
接受2个参数一个Object, 一个 Object[],其实就是来至于reflect的Method.invoke
方法2:
<T> T readValue() { List<String> list = new Arraylist<>(); list.add(“test”); return (T) list; }
用2个方法调用invoke方法,
1. invoke(“1231”, readValue());
2. List<String> list = readValue(); invoke(“12312”, list);
我们来分析一下2种不同的调用方法:
1. 第一种方法直接把readValue()的返回值当做参数传入invoke方法,这时候就需要用method context来infers readValue的返回类型,invoke method的第二个参数是Object...也就是Object[],那么通过infers就确定了readValue 的类型是Object[], oooooops, 这段代码会抛出一个ClassCastException, 因为 ArrayList can not cast to Object[], java.utils.ArrayList cannot be cast to [Ljava.lang.Object;
2. 第二个方法可以正确执行,因为我们先调用readValue方法然后赋值给list variable,这时候就有了target type, Java Compiler通过infers决定返回List<String>的值, 再把list这个变量传入invoke这个method就没有问题了.
3. 注意:这2种不同的调用方法在JDK 7的时候都能执行成功,因为JDK 7没有method context的类型推断,所以T被当成了Object,那么在readValue内部类型转换就没有问题了,因为所有的类都继承了Object。
为了更直观的看下JVM到底做了什么,我写了一个简单的小例子,然后我们看一下Java Compiler 对class字节码到底做了什么。
e.g. 1:
static void test(Object... o) { System.out.println(o); } public static void main(String[] args) { // List</String> a = gen(); // test(adf, a); test(gen()); } static <T> T gen() { List<T> list = new ArrayList<>(); //add list item return (T)list; }
这是会出现ClassCastException的代码
java.lang.ClassCastException: java.util.ArrayList cannot be cast to [Ljava.lang.Object;
e.g. 2: 这是可以执行的代码:
static void test(Object... o) { System.out.println(o); } public static void main(String[] args) { List<String> list = gen(); test(list); // test(gen()); } static <T> T gen() { List<T> list = new ArrayList<>(); //add list item return (T)list; }
这2个不同的调用方式在这个字节码里体现的很清楚,第2个调用方法生成了一个类型为list的local variable,并且是一个类型参数为String的list,参考astore_1 iconst_1, aload_1指令。
总结一下: 文章写的好不好,总结很重要
1. Type Inference就是类型推断,根据当前调用method的上下文来推断出具体的类型。
2. 如果method有一个T类型的参数,那么T的类型就由传入参数的类型决定。
3. 如果method没有类型参数,但却有一个T类型的返回,那么就要考虑context,target type,是assignment context还是method context。
4. JDK 8引入了method context的概念来实现method infers type parameters。functional interface以及Stream API大量使用method类型推断。(如果大家有兴趣,我会单独做一期关于Lambda和Stream的文章。
希望我解释的足够清楚能够帮助大家理解透彻Type Inference.
发表评论
-
Java如何把javascript格式的hex转换成Java格式的hex
2013-01-11 10:48 1691首先由于正常的ASCII码表示的字符有限,于是产生了Unico ... -
hibernate validation
2012-02-24 10:46 1695今天在项目中加入hibernate bean val ... -
JAVA位运算
2012-02-09 14:01 1244移位运算符就是在二进制的基础上对数字进行平移。按照平移的方向和 ... -
转载--------Java编码问题汇总
2011-05-31 15:23 1673转载--------Java编码问题 ... -
快速失败
2011-02-26 11:22 1946快速失败 是指某个线程 ... -
eclipse.ini内存设置
2011-01-24 12:02 2129eclipse.ini内存设置(转) -vmargs - ... -
在Windows下JAVA和ANT的环境变量设置
2009-07-23 16:42 1648我们来看一下以下设置WinXP及以上用户,右键点击我的电脑-& ... -
Java的Random
2009-06-26 09:24 4627Java中的随机数是否可以 ... -
Java中应该注意的一些特殊字符
2009-06-26 09:20 1622replace ,replaceAll 两者都是由正则表达式类 ... -
Java中String.split应该注意的问题
2009-06-26 09:20 4317今天在使用String.split方 ...
相关推荐
标题中的"Type inference for polymorphic reference"指的是针对多态引用的类型推断。在编程语言领域,多态性是允许代码可以适用于不同数据类型的一种特性。当涉及到引用(比如指针或引用传递)时,类型推断变得复杂...
Java 8中,类型推断(Type Inference)机制进一步优化了泛型的使用,使得编写代码更加简洁。另外,新引入的Optional类用于解决空指针异常问题,提供了更安全的编程方式。 三、类和对象 Java中的类是面向对象编程的...
11. **Java新特性**:随着版本更新,Java引入了许多新特性,如lambda表达式、模块化系统、类型推断(Type Inference)、接口默认方法等,这些都增强了Java的可读性和实用性。 这份“Java培训资料”可能包含了以上...
从 Java SE 10 开始,Java 语言引入了 Local-Variable Type Inference,允许开发者在声明局部变量时不需要指定变量的类型,从而使代码更加简洁、易读。 Local-Variable Type Inference 是什么? ------------------...
首先,Java SDK10中的主要更新之一是引入了局部变量类型推断(Local-Variable Type Inference),也被称为“var关键字”。这个特性允许开发者在声明局部变量时可以省略类型,编译器会自动推断出变量的类型。例如: ...
Java JDK1.7是Oracle公司...总的来说,JDK1.7是Java发展史上的一个重要里程碑,引入了一系列提升开发者效率和程序性能的新特性。然而,由于安全性和维护性的考虑,建议开发者使用最新版本的JDK以获取最佳支持和服务。
10. **Type Inference增强**:编译器现在可以更智能地推断类型,特别是在与lambda表达式和方法引用一起使用时,减少了类型声明的繁琐。 以上只是Java 1.8中部分关键特性,实际上,这个版本还包括对并发、反射、注解...
JRE1.5还引入了类型推断(Type Inference)的概念,特别是在引入匿名内部类和Lambda表达式时,大大简化了编写Java代码的过程,提升了开发效率。 在安全性方面,JRE1.5增强了沙箱模型,强化了对网络应用的安全防护,...
10. **类型推断(Type Inference)**:Java 8引入的Lambda表达式支持类型推断,简化了代码,提高了可读性。 11. **默认方法(Default Method)**:在Java 8中,接口可以拥有带有实现的默认方法,这使得在不破坏向后...
它引入了许多新特性,如类型推断(Type Inference)、钻石操作符(Diamond Operator)和多catch块等,提高了代码的可读性和编写效率。在OA系统中,JDK 1.7提供了必要的运行时环境和开发工具。 3. **MySQL 5.6**:...
2. **局部变量类型推断(Local Variable Type Inference)**:虽然这一特性最终未能出现在 Java 9 中,但它的概念为后来 Java 版本中引入的局部变量类型推断奠定了基础。 3. **流改进**:包括对 `Stream` 接口的改进...
Java 7 绿色版是Java开发环境的一种轻量级版本,主要针对Windows x86架构设计,便于在不进行完整安装的情况下运行Java应用程序。"start-windows-x86-new.bat" 文件是这个绿色版Java环境中的启动脚本,用于在Windows...
9. **Type Inference for Generic Instance Creation**:Java 8增强了类型推断,使得创建泛型实例时可以省略类型参数,编译器会自动推断。 10. **Pair 和 Map 的改进**:`Map`接口增加了`forEach()`方法,可以方便...
3. **类型推断(Type Inference for Generic Instance Creation)** 在创建泛型对象时,Java 7允许省略类型参数,编译器会根据上下文推断出正确的类型。例如,`List<String> list = new ArrayList();` 4. **字符串...
9. **Type Inference**:Java 8增强了类型推断,允许编译器在某些情况下自动推断类型,特别是在使用lambda表达式时。 10. **接口的私有方法和静态方法**:Java 8允许在接口中定义私有方法和静态方法,增强了接口的...
1. **Java版本更新**:可能涵盖了Java 10和11的主要特性,如局部变量类型推断(JEP 286: Local-Variable Type Inference)和模块系统(JEP 261: Module System),这些都是Java开发中的重大变革,影响着代码的编写...
4. **类型推断(Type Inference for Generic Instance Creation)**:不仅仅是钻石操作符,Java 7还扩展了类型推断,使得在泛型方法调用中可以更少地显式指定类型参数。 5. **文件系统API(File System API, NIO.2...
- **工具 API**:Java 9 引入了一系列新的工具 API,如 jpackage 和 jlink,用于创建自包含的应用程序和定制的 JDK 镜像。 #### 七、总结 Java 9 的发布标志着 Java 发展的一个新阶段,它不仅带来了许多新的特性和...