Java SE 8都有哪些新特性,包括哪些内容(一)?
本文内容参考自Java8标准
再次感谢尚硅谷教学资料对本文的启发!
接着上一篇博文的内容,在上一篇博文中提到,为了适应不断变化的需求(根据年龄筛选集合或者根据收入筛选集合或者根据其它内容等),我们特意创建了一个接口MyPredicate< T >来代表这种需求,同时将这个接口类型放入了主体方法的形式参数中,以便在调用主体方法的时候,能传入不同的实际需求的对象(多态,这些需求对象的类都必须implements这个接口),在最后的测试环节中引出了lambda表达式(本该传一个实际需求的对象,我们直接传入了一个lambda表达式):
①、先规定一个接口
在上一篇博文中规定这个接口是代表需求类型:MyPredicate< T >,但是在这篇博文中,这个接口是为lambda表达式准备的。
//为使用lambda表达式准备的接口,实际上它代表的还是需求。
public interface MyPredicate<T>{
//这里为什么规定方法的返回值为boolean类型
//继续往下看就知道了,因为这个方法
//需要在if语句中使用。
boolean test(T t);
}
②、再规定一个方法(WorkOnAll(List list,MyPredicate m)),这个接口是这个方法的形式参数,同时创建一个集合list,进行测试
//类FirstWithLambda
public class FirstWithLambda {
//方法WorkOnAll(List<Employees> list,
//MyPredicate<Employees> m),带两个形式参数,一个是List<Employees>
//类型的list,一个是MyPredicate<Employees>类型的m
public List<Employees> WorkOnAll(List<Employees> list,
MyPredicate<Employees> m){
//创建另外的一个List<Employees> 类型的list1接收
//m.test(e)结果为true的Employees对象
List<Employees> list1 = new ArrayList<>();
//使用foreach循环list
for(Employees e:list) {
//判断e是否符合接口MyPredicate<T>
//规定的条件,如果符合,m.test(e)结果为true,
//如果不符合,结果为false
if(m.test(e)) {
//m.test(e)结果为true,将对象e添加到
//容器list1中.
list1.add(e);
}
}
//返回容器list1
return list1;
}
}
//利用Employees对象创建一个List类型的集合(这里还使用了类
//Arrays的方法asList(T...a)进行了转换)。
//注意使用泛型
List<Employees> list = Arrays.asList(
new Employees("张三",18,9999.99),
new Employees("李四",38,5555.55),
new Employees("王五",50,6666.66),
new Employees("赵六",16,3333.33),
new Employees("田七",8,8888.88)
);
//程序执行入口main方法
public static void main(String[] args){
//创建类FirstWithoutLambda的对象
FirstWithLambda f = new FirstWithLambda();
//调用类TestAll的方法WorkOn(List<Employees> list,
//MyPredicate<Employees> m),传入实际参数list和一个一个lambda表达式
List<Employees> list11 = t.WorkOn(f.list, (e)->e.getAge()>35);
//打印容器list11
System.out.println(list11);
}
}
你会发现,在需要传一个接口类型的实际调用中,只传入了一个lambda表达式就解决了问题:
这就是lambda表达式看起来的样子:(e)->e.getAge()>35
重点是这个符号:"->",它是lambda表达式特有的。
那么这个符号的左边(e)和右边(e.getAge()>35)分别是什么呢?
实际上,lambda表达式符号("->")左边的部分是它的参数,而右边的部分是lambda体,也就是这个lambda表达式需要执行的代码。
那么lambda表达式的参数和lambda体分别有什么规则呢?分以下几步来看:
⑴、lambda表达式需要一个"函数式"接口的支持。
什么叫"函数式"接口呢?“函数式"接口指的是只有一个方法的接口(也就是这个接口里面只有一个方法需要实现),比如上面的代码示例中的接口(MyPredicate< T >):它只有一个需要实现的方法(test(T t))。
那么,接下来的内容就很简单了。
再回到上面代码示例中的主体方法(WorkOnAll(List< Employees > list, MyPredicate< Employees > m)),在调用它的时候,需要传入两个实际参数,一个是List< Employees >类型的list对象,一个是实现了MyPredicate< Employees >接口的类的对象,list对象好理解,重点在:实现了MyPredicate< Employees >接口的类的对象。
在上一篇博文中,我分别尝试了三种方式:
①、用一个类去实现接口MyPredicate< Employees >,然后创建这个类的对象作为实际参数传入方法WorkOnAll(List< Employees > list, MyPredicate< Employees > m)。
②、在调用方法WorkOnAll(List< Employees > list, MyPredicate< Employees > m)的时候直接使用一个匿名内部类。
③、在调用方法WorkOnAll(List< Employees > list, MyPredicate< Employees > m)的时候直接使用一个lambda表达式。
你会发现,在以上三种方式中,接口MyPredicate< Employees >的方法test(T t)的形式参数t都是重点,无论哪种方式它都是核心,而lambda表达式是最简代码表示,所以:lambda表达式符号”->"左边的内容是传入这个函数式接口的方法的实际参数(接口MyPredicate< Employees >的方法test(T t)使用了泛型,所以用T表示,而实际传入的参数是类Employees对象,所以最终的lambda表达式中的e表示的就是一个类Employees的对象)。
图示
1、一个类implements接口MyPredicate< Employees >的参数
2、匿名内部类的参数
3、lambda表达式的参数
你还会发现,在以上三种方式中,接口MyPredicate< Employees >的方法test(T t)的实现是除参数t外的另一个重点,无论使用哪种方式,你总是需要为方法test(T t)提供一个实现的,而lambda方式仍然是最简代码表示。
图示
1、一个类implements接口MyPredicate< Employees >的实现
2、匿名内部类的实现
3、lambda表达式的实现
你会发现lambda表达式是实现一个函数式接口的最简方式,一句多余的代码都没有(仅有参数和实现,实际上,它除了一个特有的符号,也没有任何的关键字)。
⑵、lambda表达式的参数数量与函数式接口的方法中声明的形式参数的数量完全一致。
因为方法除了参数以外,还涉及到是否有返回值的问题,下面通过举例来说明所有的情况
①、函数式接口的方法既没有形式参数,也没有返回值。
// 方法既没有参数,也没有返回值的函数式接口
public interface MyPredicate<T>{
void test();
}
//-----------------------------------------------------
//lambda表达式的使用形式
()->System.out.println("Hello Lambda");
//没有任何参数,lambda表达式左边就用()表示,右边就随意提供一些实现就行
//在标准JavaAPI中,接口Runnable就属于这种类型
//没有lambda表达式时使用接口Runnable的代码:
Runnable r = new Runnable(){
public void run(){
System.out.println("Hello Lambda");
}
};
//通过lambda表达式使用Runnable接口
Runnable r1 = ()->System.out.println("Hello Lambda");
结果示例: