Java基础知识-Java10新特性

局部变量类型推断

1. 背景

2018年3月21日,Oracle官方宣布Java10正式发布。

  • 需要注意的是 Java 9 和 Java 10 都不是 LTS (Long-Term-Support) 版本。和 过去的 Java 大版本升级不同,这两个只有半年左右的开发和维护期。而未 来的 Java 11,也就是 18.9 LTS,才是 Java 8 之后第一个 LTS 版本。

  • JDK10一共定义了109个新特性,其中包含12个JEP(对于程序员来讲,真正的新特性其实就一个),还有一些新API和JVM规范以及JAVA语言规范上 的改动。

  • JDK10的12个JEP(JDK Enhancement Proposal特性加强提议)参阅官方文档:http://openjdk.java.net/projects/jdk/10/

2. JDK10的12个JEP

286: Local-Variable Type Inference 局部变量类型推断

296: Consolidate the JDK Forest into a Single Repository JDK库的合并

304: Garbage-Collector Interface 统一的垃圾回收接口

307: Parallel Full GC for G1 为G1提供并行的Full GC

310: Application Class-Data Sharing 应用程序类数据(AppCDS)共享

312: Thread-Local Handshakes ThreadLocal握手交互

313: Remove the Native-Header Generation Tool (javah) 移除JDK中附带的javah工具

314: Additional Unicode Language-Tag Extensions 使用附加的Unicode语言标记扩展

316: Heap Allocation on Alternative Memory Devices 能将堆内存占用分配给用户指定 的备用内存设备

317: Experimental Java-Based JIT Compiler 使用基于Java的JIT编译器

319: Root Certificates 根证书

322: Time-Based Release Versioning 基于时间的发布版本

3. 局部变量类型推断

3.1. 产生背景

开发者经常抱怨Java中引用代码的程度。局部变量的显示类型声明,常常被认为是不必须的,给一个好听的名字经常可以很清楚的表达出下面应该怎样继续。

3.2. 好处

减少了啰嗦和形式的代码,避免了信息冗余,而且对齐了变量名,更容易阅读

3.3. 举例

适用于以下情况:

1
2
3
4
5
6
7
8
9
10
11
//1.局部变量的初始化
var list = new ArrayList<>();
//2.增强for循环中的索引
for(var v : list) {
System.out.println(v);
}

//3.传统for循环中
for(var i = 0;i < 100;i++) {
System.out.println(i);
}
  • 场景一:类实例化时

    作为 Java开发者,在声明一个变量时,我们总是习惯了敲打两次变量类型,第一次用于声明变量类型,第二次用于构造器。

    1
    LinkedHashSet<Integer> set = new LinkedHashSet<>(); 
  • 场景二:

返回值类型含复杂泛型结构

变量的声明类型书写复杂且较长,尤其是加上泛型的使用

1
Iterator<Map.Entry<Integer, Student>> iterator = set.iterator();
  • 场景三:

    我们也经常声明一种变量,它只会被使用一次,而且是用在下一行代码中,比如:

    1
    2
    3
    URL url = new URL("http://www.runaccepted.com"); 
    URLConnection connection = url.openConnection();
    Reader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));

    尽管 IDE可以帮我们自动完成这些代码,但当变量总是跳来跳去的时候,可读性还是会受到影响,因为变量类型的名称由各种不同长度的字符组成。而且,有时候开发人员会尽力避免声明中间变量,因为太多的类型声明只会分散注意力,不会带来额外的好处。

3.4. 不适用以下的结构中

  • 情况1:没有初始化的局部变量声明
  • 情况2:方法的返回类型
  • 情况3:方法的参数类型
  • 情况4:构造器的参数类型
  • 情况5:属性
  • 情况6:catch块

3.5. 工作原理

在处理 var时,编译器先是查看表达式右边部分,并根据右边变量值的类型进行推断,作为左边变量的类型,然后将该类型写入字节码当中。

3.6. 注意

  • var不是一个关键字
    你不需要担心变量名或方法名会与 var发生冲突,因为 var实际上并不是一个关键字, 而是一个类型名,只有在编译器需要知道类型的地方才需要用到它。除此之外,它就是一个普通合法的标识符。也就是说,除了不能用它作为类名,其他的都可以,但极少人会用它作为类名。

  • 这不是JavaScript 首先我要说明的是,var并不会改变Java是一门静态类型语言的事实。编译器负责推 断出类型,并把结果写入字节码文件,就好像是开发人员自己敲入类型一样。下面是使用 IntelliJ(实际上是 Fernflower的反编译器)反编译器反编译出的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    var url = new URL("http://www.runaccepted.com"); 
    var connection = url.openConnection();
    var reader = new BufferedReader(
    new InputStreamReader(connection.getInputStream()));

    //======反编译后=========
    URL url = new URL("http://www.runaccepted.com");
    URLConnection connection = url.openConnection();
    BufferedReader reader = new BufferedReader(
    new InputStreamReader(connection.getInputStream()));

    从代码来看,就好像之前已经声明了这些类型一样。事实上,这一特性只发 生在编译阶段,与运行时无关,所以对运行时的性能不会产生任何影响。所 以请放心,这不是 JavaScript。

4. 集合新增创建不可变集合的方法

自 Java 9 开始,Jdk 里面为集合(List / Set / Map)都添加了of (jdk9新增)和 copyOf (jdk10新增)方法,它们两个都用来创建不可变的集合,来看下它们的使用和区别。

1
2
3
4
5
6
7
8
9
10
11
//示例1:
var list1 = List.of("Java", "Python", "C");
var copy1 = List.copyOf(list1);
System.out.println(list1 == copy1); // true

//示例2:
var list2 = new ArrayList<String>();
var copy2 = List.copyOf(list2);
System.out.println(list2 == copy2); // false

//示例1和2代码基本一致,为什么一个为true,一个为false?

List.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public interface List<E> extends Collection<E> {
@SafeVarargs
@SuppressWarnings("varargs")
static <E> List<E> of(E... elements) {
switch (elements.length) { // implicit null check of elements
case 0:
@SuppressWarnings("unchecked")
var list = (List<E>) ImmutableCollections.ListN.EMPTY_LIST;
return list;
case 1:
return new ImmutableCollections.List12<>(elements[0]);
case 2:
return new ImmutableCollections.List12<>(elements[0], elements[1]);
default:
return new ImmutableCollections.ListN<>(elements);
}
}

static <E> List<E> copyOf(Collection<? extends E> coll) {
return ImmutableCollections.listCopy(coll);
}
}

ImmutableCollections.java

1
2
3
4
5
6
7
8
9
10
11
12
@SuppressWarnings("serial")
class ImmutableCollections {
@SuppressWarnings("unchecked")
static <E> List<E> listCopy(Collection<? extends E> coll) {
if (coll instanceof AbstractImmutableList
&& coll.getClass() != SubList.class) {
return (List<E>)coll;
} else {
return (List<E>)List.of(coll.toArray());
}
}
}

从源码分析,可以看出 copyOf 方法会先判断来源集合是不是 AbstractImmutableList 类型的,如果是,就直接返回,如果不是,则调用 of 创建一个新的集合。

示例2因为用的 new 创建的集合,不属于不可变 AbstractImmutableList 类的子类, 所以 copyOf 方法又创建了一个新的实例,所以为false。

注意:使用of和copyOf创建的集合为不可变集合,不能进行添加、删除、替换、 排序等操作,不然会报 java.lang.UnsupportedOperationException 异常。

上面演示了 List 的 of 和 copyOf 方法,Set 和 Map 接口都有。

本文结束  感谢您的阅读

Gitalking ...