Java1.8Lambda

文章目录

  • Java1.8-Lambda表达式
    • 表现形式
    • 为什么用
    • 如何用
    • 思考

Java1.8-Lambda表达式

简单概括:Java1.8之前方法仅能传递值(变量、对象的引用等),1.8之后方法内可以传递行为、代码或者表达式。这就是Lambda表达式所实现的功能。你可以将Lambda表达式想象成为匿名函数中方法的具体执行过程以便理解。其特点可以用简洁来形容。

表现形式

(parameters) -> expression 
或者
(parameters) -> { statements; }
():中表明参数列表,单个参数时可省略括号。参数的类型也可省略。
->:将参数列表和Lambda进行分割的符号
{}:lambda主体,当主体为表达式时可省略,为执行语句时不可省略。你可以这样记,有分号带大括号,无分号可省略。
其实我感觉不用过多理解应不应该省略,你就都不省略肯定没错。如:
(Person p)->p.getName()
p->p.getName()
p-{return p.getName();}
这三个都是等价。

为什么用

//甲方需求1:一个果农需要对绿色苹果进行筛选。
//代码如下:简单明了应该都能读的懂
public static List<Apple> filterGreenApples(List<Apple> inventory) {
	List<Apple> result = new ArrayList<Apple>();
	for(Apple apple: inventory){
		if( "green".equals(apple.getColor() ) {//核心代码
			result.add(apple);
		}
	}
	return result;
}
//调用代码
List<Apple> greenApples = filterGreenApples(苹果list);
//甲方需求2:对红色苹果进行筛选
//如果你在写一个filterRedApples,那么除上述核心代码外都将是重复代码,你变聪明了,你将颜色参数传入方法改成了如下代码
public static List<Apple> filterApplesByColor(List<Apple> inventory,String color) {
	List<Apple> result = new ArrayList<Apple>();
	for (Apple apple: inventory){
		if ( apple.getColor().equals(color) ) {//核心代码
		result.add(apple);
		}
	}
	return result;
}
//调用代码
List<Apple> redApples = filterGreenApples(苹果list,“红色”);
List<Apple> greenApples = filterGreenApples(苹果list,“绿色”);
//甲方需求3:对重量超过150的苹果进行筛选,直接将重量进行参数化
public static List<Apple> filterApplesByWeight(List<Apple> inventory,int weight) {
	List<Apple> result = new ArrayList<Apple>();
	for (Apple apple: inventory){
		if ( apple.getWeight() > weight ){//核心代码
			result.add(apple);
		}
	}
	return result;
}
//你感觉上述代码不太满意,觉得代码重复率还是比较高。对代码进行改进。
public static List<Apple> filterApples(List<Apple> inventory, String color,int weight, boolean flag) {
	List<Apple> result = new ArrayList<Apple>();
		for (Apple apple: inventory){
			if ( (flag && apple.getColor().equals(color)) ||(!flag && apple.getWeight() > weight) ){//核心代码
				result.add(apple);
			}
		}
	return result;
}
//代码调用
List<Apple> greenApples = filterApples(苹果list, "green", 0, true);
List<Apple> weightApples = filterApples(苹果list, "", 150, false);
//这样做可以吗???,可以!但是,不易读,维护不方便,可能后期你自己读都要想一会逻辑思路。
/*
通过上述需求以及代码的编写你会发现,核心代码其实就一行,你想到了定义一个公用接口,利用实现类进行筛选
*/
public interface ApplePredicate{
	boolean test (Apple apple);
}
//方法定义
public static List<Apple> filterApples(List<Apple> inventory,ApplePredicate p){
	List<Apple> result = new ArrayList<>();
	for(Apple apple: inventory){
		if(p.test(apple)){
			result.add(apple);
		}
	}	
	return result;
}
//你可以有两种方法进行实现方法实现
方法1:定义实现类,通过重量筛选
public class AppleHeavyWeightPredicate implements ApplePredicate{
	public boolean test(Apple apple){
		return apple.getWeight() > 150;
	}
}
List<Apple> weightApples =filterApples(苹果list, new AppleHeavyWeightPredicate ())
方法2:匿名内部类
filterApples(苹果list,new ApplePredicate(){
	public boolean test(Apple apple){
		return apple.getWeight() > 150;
	}
});
//但是你想我为一个方法定义一个接口是不是很繁琐,正巧的是1java.8正好提供了几个接口以便大家利用。
//如Predicate,其抽象方法为boolean test(T t);。正符合筛选苹果的需求设定。
//还有Function,方法R apply(T t),
//Consumer方法void accept(T t)。等。
//所以在不用定义接口的前提下最终代码如下
filterApples(苹果list,new Predicate<Apple>(){
	public boolean test(Apple apple){
		return apple.getWeight() > 150;
	}
});

先不讲lambda原理,看一下采用lambda表达式来重写上述代码

filterApples(苹果list,(Apple apple)->apple.getWeight() > 150);

是不是简单、灵活、易读。你从上述描述就可以明显看出,此次传递的是一个行为
但是你觉得filterApples这样的方法定义是不是也很繁琐,我不能再对动物、物品、植物等不同类型进行筛选时每次都定义一个吧。那么你可以利用泛型以及java1.8预先定义的接口,代码如下:

public static <T> List<T> filter(List<T> list, Predicate<T> p){
	List<T> result = new ArrayList<>();
	for(T e: list){
		if(p.test(e)){
		result.add(e);
		}
	}
	return result;
}

如下举几个例子

//筛选红苹果
List<Apple> colorList = filter(苹果list,(Apple apple)->apple.getColor().equasl("red"));
//对一个integer类型的list筛选偶数
List<Integer> list = filter(数字list,(Integer i)->i%2==0);
//对苹果按重量排序
List<Apple> sortWeightList = 苹果list.sort((Apple a1,Apple a2)->a1.getWeigth().compareTo(a2.getWeight()));

如何用

上述最后编写了filter方法,那除了这样用,还能怎么用呢。用在函数式接口
什么是函数式接口,通俗易懂的解释就是一个接口中只有一个抽象方法
java1.8之前接口中不能有实现方法,但是1.8引入了default关键字可以对方法进行实现,但这并不是抽象方法。在接口中用@FunctionalInterface进行注解的均为函数式接口,但是不注解也可能是,注解的加入只是检查你的接口是否符合函数式接口的定义。在java.util.function包下有function、consumer、predicate等函数式接口。昨天发现在Comparator接口中有两个抽象方法但是该接口为函数式接口,其中有一个为equals,网上解释为该方法是重写object类方法,因此equals不算本接口的方法。

@FunctionalInterface
public interface Predicate<T> {
	boolean test(T t);
	//省略default方法
}
filter(苹果list,(Apple a)->a.getWeight()>150);
//函数式接口的抽象方法的签名即为lambda的签名,test方法的函数描述符为T,意味着可以是任意类型,则lambda为Apple类型
//抽象方法的返回值为boolean,lambda的返回为return a.getWeight()>150;也为boolean。
像Function<T,R>中R apply(T t)为 ,则可以代表为传入T类型,返回R类型。

思考

filter(苹果list,(Apple a)->a.getWeight()>150);可以写成
filter(苹果list,a->a.getWeight()>150);
而filter方法的参数为list<T>和Predicate<T> p。
为什么a->a.getWeight()>150就能等价Predicate<T> p呢。
其实lambda使用类型是从上下文进行推断的。,比如接受它传递的方法的参数,或接受它的值的局部变量

如下为java8实战书籍上一个解释图以及步骤图示:图中inventory相当于传入的list
Java1.8Lambda_第1张图片
Java1.8Lambda_第2张图片
作者也是一个菜鸟,以上内容是在看完Java8实战这本书对lambda表达式的一个简单总结。当然书中讲的内容远多于此。

你可能感兴趣的:(Java)