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

相关内容

热门资讯

常用商务英语口语   商务英语是以适应职场生活的语言要求为目的,内容涉及到商务活动的方方面面。下面是小编收集的常用商务...
六年级上册英语第一单元练习题   一、根据要求写单词。  1.dry(反义词)__________________  2.writ...
复活节英文怎么说 复活节英文怎么说?复活节的英语翻译是什么?复活节:Easter;"Easter,anniversar...
2008年北京奥运会主题曲 2008年北京奥运会(第29届夏季奥林匹克运动会),2008年8月8日到2008年8月24日在中华人...
英语道歉信 英语道歉信15篇  在日常生活中,道歉信的使用频率越来越高,通过道歉信,我们可以更好地解释事情发生的...
六年级英语专题训练(连词成句... 六年级英语专题训练(连词成句30题)  1. have,playhouse,many,I,toy,i...
上班迟到情况说明英语   每个人都或多或少的迟到过那么几次,因为各种原因,可能生病,可能因为交通堵车,可能是因为天气冷,有...
小学英语教学论文 小学英语教学论文范文  引导语:英语教育一直都是每个家长所器重的,那么有关小学英语教学论文要怎么写呢...
英语口语学习必看的方法技巧 英语口语学习必看的方法技巧如何才能说流利的英语? 说外语时,我们主要应做到四件事:理解、回答、提问、...
四级英语作文选:Birth ... 四级英语作文范文选:Birth controlSince the Chinese Governmen...
金融专业英语面试自我介绍 金融专业英语面试自我介绍3篇  金融专业的学生面试时,面试官要求用英语做自我介绍该怎么说。下面是小编...
我的李老师走了四年级英语日记... 我的李老师走了四年级英语日记带翻译  我上了五个学期的小学却换了六任老师,李老师是带我们班最长的语文...
小学三年级英语日记带翻译捡玉... 小学三年级英语日记带翻译捡玉米  今天,我和妈妈去外婆家,外婆家有刚剥的`玉米棒上带有玉米籽,好大的...
七年级英语优秀教学设计 七年级英语优秀教学设计  作为一位兢兢业业的人民教师,常常要写一份优秀的教学设计,教学设计是把教学原...
我的英语老师作文 我的英语老师作文(通用21篇)  在日常生活或是工作学习中,大家都有写作文的经历,对作文很是熟悉吧,...
英语老师教学经验总结 英语老师教学经验总结(通用19篇)  总结是指社会团体、企业单位和个人对某一阶段的学习、工作或其完成...
初一英语暑假作业答案 初一英语暑假作业答案  英语练习一(基础训练)第一题1.D2.H3.E4.F5.I6.A7.J8.C...
大学生的英语演讲稿 大学生的英语演讲稿范文(精选10篇)  使用正确的写作思路书写演讲稿会更加事半功倍。在现实社会中,越...
VOA美国之音英语学习网址 VOA美国之音英语学习推荐网址 美国之音网站已经成为语言学习最重要的资源站点,在互联网上还有若干网站...
商务英语期末试卷 Part I Term Translation (20%)Section A: Translate ...