Java8新特性详解
创始人
2024-06-03 08:22:06
0

一,Lambda表达式

二,函数式接口

三,方法引用与构造器引用

四,Stream API

五,接口中的默认方法与静态方法

六,新的日期、时间API

七,其他新特性

Lambda表达式

  1. lambda是什么?

官方的概念就不说了,不太好理解,以我自己的理解而言,它就是一种简写的匿名内部类。

先简单对比一下:

1.1 使用匿名内部类实现Comparator

Comparator comparator1 = new Comparator() {@Overridepublic int compare(Integer o1, Integer o2) {return o1.compareTo(o2);}
};

1.2 使用Lambda 实现Comparator

Comparator comparator2 = (o1,o2) -> o1.compareTo(o2);

实际上就是对匿名内部类进一步进行了代码量的削减,省略了类名、方法名、参数类型,只保留形参和方法体,如下图:

1.3 为什么类名、方法名、参数类型可以省略? 因为进行了自动推导, 看这句代码:

Comparator comparator2 = (o1,o2) -> o1.compareTo(o2);

1.3.1 对于类名的省略

因为 等号 左边已经写了类型是Comparator,所以可以推导出 右边的匿名子对象一定是Comparator的实现类,所以可以省略类名;

1.3.2 对于方法名的省略

因为 Comparator 接口中,仅有一个需要实现的方法,所以方法名也是可以推导出来的,这也是Lambda使用的一个限制,那就是对于接口而言,仅只有一个需要实现的方法时才能用Lambda表达式,

只有一个抽象方法的interface称之为 "函数式接口",给interface加上注解 @FunctionalInterface可强制申明为 函数式接口,若定义多个抽象方法,就会报编译错误,如下:

1.3.2.1只定义一个方法时不报错:

1.3.2.2定义多个方法时就报错了:

1.3.3 对于参数类型的省略

既然类名、方法名都可以推导出来,那么方法的参数类型也是可以推导出来的,所以参数类型也可以省略, 那为什么参数名不能省略呢? 因为你需要在方法体中使用参数,没有参数名,你咋使用这个参数了,比如compare方法的o1.compareTo(o2), 如果不保留参数名o1和o2,这个比较方法就没法写了;

当然参数类型也是可以保留的,提升阅读性,比如这样写也是可以的:

Comparator comparator2 = (Integer o1,Integer o2) -> o1.compareTo(o2);

1.3.4 小结

所以,我对于Lambda的理解,就认为是对匿名内部类的一种能省则省的简写(语法糖),它包含一个右箭头,箭头左边是参数,箭头右边是方法体,在不同场景,它有一些不同的写法,下面具体介绍一下它的不同写法

2,Lambda表达式的几种写法

2.1 场景一:对于 无入参 、 无返回值、方法体仅有一句代码 的方法

例如Runnable的run方法,可以这样实现:

//使用匿名内部类实现Runnable
Runnable runnable1 = new Runnable() {@Overridepublic void run() {System.out.println("匿名内部类创建线程");}
};
new Thread(runnable1).start();//使用Lambda实现Runnable
Runnable runnable2 = ()-> System.out.println("Lambda创建线程");
new Thread(runnable2).start();//也可直接写成一行,看起来就像把一段代码在作为参数传递
new Thread(()-> System.out.println("Lambda创建线程")).start();

因为没有入参,但是箭头左边必须有参数,所以左边仅写一对小括号即可,箭头右边是方法体,因为只有一句代码,所以方法体的 大括号 也省略了,这里给方法体加上大括号也是可以的。

2.2 场景二:对于 只有1个入参、无返回值、方法体仅有一句代码 的方法

例如实现 java.util.function.Consumer 类,如下:

//使用匿名内部类实现 java.util.function.Consumer 类
Consumer consumer1= new Consumer() {@Overridepublic void accept(Integer x) {System.out.println(x);}
};//使用Lambda实现 java.util.function.Consumer 类
Consumer consumer2 = x-> System.out.println(x);//使用Lambda实现 java.util.function.Consumer 类,也可以保留参数类型
Consumer consumer2 = (Integer x)-> System.out.println(x);

因为只有一个参数,所以连方法的小括号也省略了;

2.3 场景三:对于多个入参、有返回值、方法体只有一句代码的

例如上面的 Comparator 接口,如下:

//匿名内部类实现
Comparator comparator1 = new Comparator() {@Overridepublic int compare(Integer o1, Integer o2) {return o1.compareTo(o2);}
};//Lambda实现, 省略return
Comparator comparator2 = (o1,o2) -> o1.compareTo(o2);//Lambda实现, 不省略return
Comparator comparator3 = (o1,o2) -> {return o1.compareTo(o2);};

因为有多个入参,所以多个入参都需要写出来,此时小括号不能省略;

由于方法体只有一句代码,所以方法体的大括号可以省略;

注意:

当方法体只有一句代码时,return 也可以省略,因为就一句代码,如果需要return,那么return的一定是这句代码的结果,所以return可以省略,但是这样写是错误的:

//这样写是错误的
Comparator comparator3 = (o1,o2) -> return o1.compareTo(o2);

但如果是多句代码,return 和 大括号都不可省略。

2.4 场景四:对于多个入参、有返回值、方法体有多句代码的

例如上面的 Comparator 接口,如下:

Comparator comparator3 = (o1,o2) -> {System.out.println("这是多行代码的示例");return o1.compareTo(o2);
};

此时方法体的大括号、return 都不能省略。

2.5 小结

对于上述几种场景,不用死记硬背,只是在说参数的括号、方法体的括号、return 什么时候可以省略,如果记不住不省略也是可以的,写的时候开发工具也是有提示的,不用纠结。

3,Lambda表达式的四大核心函数式接口

3.1

3.1 先说下为什么要有这4个接口,先看下面的铺垫便于理解,如果不想看,直接跳到3.2

比如我们想实现一个功能,即实现对一个数字的处理,比如取平方、取立方、取负数等

第一种做法,每种处理写一个方法:

public static void main(String[] args) {pingFang(5);liFang(5);fuShu(5);
}
//取平方
public static int pingFang(int x){return x*x;
}//取立方
public static int liFang(int x){return x*x*x;
}//取负数
public static int fuShu(int x){return -x;
}

第二种做法,定义一个接口,处理逻辑写在接口的实现类中(策略模式)

//定义一个接口
public interface NumberProcess {int process(int x);
}//测试类
public class Test{public static void main(String[] args) {//取平方int res= calc(5, new NumberProcess() {@Overridepublic int process(int x) {return x*x;}});//取立方res= calc(5, new NumberProcess() {@Overridepublic int process(int x) {return x*x*x;}});}//定义一个公共的计算方法public static int calc(int x,NumberProcess p){return p.process(x);}
}

第二种比第一种稍好一些,不需要有N个处理就写N个方法,但是需要额外定义一个接口,如果能把这个接口省略就更好了,所以java就定义了一些通用功能的接口,比如接口 java.util.function.Function;

其源码如下:

就是定义了一个apply方法,该接口有两个泛型,第一个泛型T是入参类型,第二个泛型R是返回值类型,所以我们就可以不用自己定义上面的NumberProcess接口了,使用Function接口代替,因此有了第三种做法。

第三种做法,使用Function接口,比如取平方:

入参和返回值都是Integer类型

//测试类
public class Test{public static void main(String[] args) {//取平方int res = calc(5, new Function() {@Overridepublic Integer apply(Integer x) {return x*x;}});//取立方res = calc(5, new Function() {@Overridepublic Integer apply(Integer x) {return x*x*x;}});}//定义一个公共的计算方法public static int calc(int x,Function f){return f.apply(x);}
}

这个写法,就可以使用Lambda进行简写,由此产生第四种做法。

第四种做法,使用Lambda对第三种进行简写,如下:

//测试类
public class Test{public static void main(String[] args) {//取平方int res = calc(5, x -> x*x);//取立方res = calc(5, x -> x*x*x);}//定义一个公共的计算方法public static int calc(int x,Function f){return f.apply(x);}
}

小结:

为了简化一些策略模式的功能开发,java为我们提供了4种接口,上面用到的Function接口就是其中之一,让我们不需要再定义自己的接口就可以实现一些策略模式的功能。

3.2 有哪4个核心函数式接口?各有什么用途?

这4个接口是按照入参和返回值来设定的

① java.util.function.Consumer;

有一个入参,无返回值时可以使用这个接口, 之所以叫消费者,就是因为它只进不出,比如上面3.1的例子就不适合使用它,因为3.1的例子有入参也有返回值(传入一个数字x,返回x的平方或立方)

② java.util.function.Supplier;

无入参,但是有返回值时可以使用这个接口,之所以叫提供者,就是因为它能无中生有,比如产生随机数的方法就可以用它来做

//产生随机数的例子,当然这里是为了使用而使用,旨在演示
Supplier supplier = ()-> new Random().nextInt();
int randNum = supplier.get();

③ java.util.function.Function;

有一个入参和一个返回值,使用可参考3.1的第四种做法

④ java.util.function.Predicate;

一个入参,返回值为boolean, 用来实现一些判断逻辑

小结:

这四大核心接口先了解即可,它们在Stream API中会大量使用。

4,方法引用、构造器引用、数组引用

4.1 方法引用

就是双冒号的用法( :: 的用法), 怎么理解它呢?看个例子:

//User类
public class User {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}//测试方法,用到了java.util.function.Supplier类
public static void main(String[] args) {User user = new User();user.setName("tom");//使用匿名内部类写Supplier supplier2 = new Supplier() {@Overridepublic String get() {return user.getName();}};String name = supplier2.get();//使用方法引用进行改写,引用user类的getName方法作为Supplier的实现Supplier supplier = user::getName;name = supplier.get();
}

实例中使用了两种写法,是等效的, 我对于方法引用的理解:

它也是一种Lambda表达式,前面说Lambda是一种匿名内部类的简写,那么 “方法引用”既然也是一种Lambda表达式,它必然也是一种匿名内部类的简写,即对接口实现的简写,只不过以往的 接口实现代码 都是我们自己在写,而这个“方法引用” 就是我们不用自己写接口实现的代码了,直接告诉它接口的实现代码 是哪个类的哪个方法(告诉它Supplier接口的实现代码就是user对象的getName方法),对象和方法名之间用双冒号隔开。

注意:引用某个对象的某个方法作为 接口的实现代码,那么一定要保证: 接口的方法的入参类型和返回值类型 一定要与你引用的方法 的入参类型和返回值类型 一样,否则不可以引用,如下:

//User类
public class User {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}//MyInterface 接口
@FunctionalInterface
public interface MyInterface {String hello();
}

测试方法如下:

可以看到user::getAge报错了,该方法不能作为MyInterface接口的实现,因为MyInterface的返回值类型是String,而getAge返回int,所以getAge不能被引用做MyInterface的实现方法;

方法引用有三种使用方式:

① 对象::实例方法名

示例:

public class User {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}public static void main(String[] args) {User user = new User();user.setName("tom");//引用user对象的getName方法作为Supplier的实现Supplier supplier = user::getName;String name = supplier.get();
}

方法引用时,方法的入参是可以省略的,把上面的user.setName用Consumer接口改写,加深理解,如下:

public static void main(String[] args) {User user = new User();//引用user对象的setName方法作为Consumer的实现Consumer consumer = user::setName;consumer.accept("Tom");//类似调用了user。setName("Tom")//引用user对象的getName方法作为Supplier的实现Supplier supplier = user::getName;String name = supplier.get();System.out.println("name="+name);
}

方法引用,说简单点就是不用写接口的实现代码了,直接引用某个类的某个方法作为实现代码。

使用条件:

接口的方法的入参类型和返回值类型 一定要与你引用的方法 的入参类型和返回值类型 一样。

② 类名::静态方法名

public static void main(String[] args) {//这两种写法等价Comparator comparator1 = (x,y) -> Integer.compare(x,y);Comparator comparator2 = Integer::compare;
}

使用条件:

接口的方法的入参类型和返回值类型 一定要与你引用的方法 的入参类型和返回值类型 一样。

③ 类名::实例方法名

public static void main(String[] args) {//这三种写法等价Comparator comparator1 = new Comparator() {@Overridepublic int compare(String o1, String o2) {return o1.compareTo(o2);}};Comparator comparator2 = (x,y) -> x.compareTo(y);Comparator comparator3 = String::compareTo;}

使用条件:

1) 接口的方法的入参类型和返回值类型 一定要与你引用的方法 的入参类型和返回值类型 一样。入参都是2个String类型,返回值都是int类型

2) 都有2个入参,且第一个参数用来调用 引用的方法,将第二个参数作为被引用方法的入参传入

可以看出这种使用方式的条件更为特殊.

4.2 ,构造器引用

一种特殊的方法引用,用法:

类名::new

示例:

//User对象
public class User {private String name;private int age;public User() {}public User(String name) {this.name = name;}public User(String name, int age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}//测试方法
public static void main(String[] args) {//因为Supplier接口的方法是无参的,因此匹配User的无参构造Supplier supplier1 = User::new;User user1 = supplier1.get();//因为Function接口的方法有一个入参,且泛型为String,因此匹配User(String) 的构造Function function1 = User::new;User user2 = supplier1.get();//因为BiFunction接口的方法有2个入参,且入参泛型为String和Integer,因此匹配User(String,Integer) 的构造BiFunction function2 = User::new;User user3 = supplier1.get();
}

4.3,数组引用

一种特殊的方法引用,用于创建数组,用法:

数组类型[]::new

public static void main(String[] args) {//创建一个长度为x的User[]几种写法//第一种User[] xx = new User[3];//第二种Function function1 = x->new User[x];xx = function1.apply(3);//第三种,数组引用Function function2 = User[]::new;xx = function2.apply(3);}

相关内容

热门资讯

师德师风演讲比赛主持稿 师德师风演讲比赛主持稿范文(通用7篇)  在快速变化和不断变革的今天,很多地方都会使用到主持稿,主持...
结婚仪式主持词 结婚仪式主持词10篇  主持词是各种演出活动和集会中主持人串联节目的串联词。我们眼下的社会,主持人参...
企业年会主持词开场白精选   新年拉近了我们成长的距离,新年染红了我们快乐的生活。下面是CN人才网小编为大家整理的年会主持词开...
教师节座谈会校长致辞 教师节座谈会校长致辞(精选16篇)  在日常学习、工作抑或是生活中,大家对致辞都再熟悉不过了吧,在各...
五一晚会开幕词与闭幕词 五一晚会开幕词与闭幕词  在当今社会生活中,我们经常都会使用到开幕词,开幕词对引导会议或活动顺利进行...
含笑半步颠经典台词 含笑半步颠经典台词  在社会发展不断提速的今天,我们使用上台词的情况与日俱增,台词是构成一个剧本的基...
联谊会主持词 有关联谊会主持词集合8篇  主持词是各种演出活动和集会中主持人串联节目的串联词。在人们积极参与各种活...
晚会闭幕词 晚会闭幕词(通用37篇)  在日新月异的现代社会中,很多情况下我们需要用到主持稿,主持稿起到承上启下...
婚礼主持词 婚礼主持词(精选20篇)  主持词要注意活动对象,针对活动对象写相应的主持词。在人们越来越多的参与各...
半年总结会主持词 半年总结会主持词  以下是由应届毕业生网PQ小编为大家整理出来的半年总结会主持词,仅供参考,半年总结...
最短的对口相声台词 最短的对口相声台词范文  相声是一种中国曲艺表演艺术,源于华北,流行于京津冀,普及于全国及海内外,始...
司仪主持词 精选司仪主持词(精选14篇)  主持词需要富有情感,充满热情,才能有效地吸引到观众。在各种集会、活动...
电影节颁奖典礼主持词 电影节颁奖典礼主持词  颁奖典礼上最重要的就是主持人手中的台词啦!下面来看看小编带来的电影节颁奖典礼...
安全生产会议的致辞 安全生产会议的致辞(精选5篇)  在日常的学习、工作、生活中,要用到致辞的地方还是很多的,致辞具有“...
最新半台词分享 最新三句半台词分享  俺们几个话挺多,大家不要嫌罗嗦,希望能够捧捧场,鼓掌!  北京先把地方占,天津...
《教父》经典台词中英文对照 《教父》经典台词中英文对照  1、To be close to your friend, but c...
播音主持稿 播音主持稿(精选21篇)  在现在的社会生活中,我们很多时候都不得不用到主持稿,主持稿是主持人为把整...
年会主持词 精选年会主持词四篇  主持词要注意活动对象,针对活动对象写相应的主持词。在现今人们越来越重视活动氛围...
金秋国庆主持词开场白 金秋国庆主持词开场白  国庆节是我们祖国母亲的生日,下面unjs小编整理了金秋国庆主持词开场白,欢迎...