JDK8中能够添加了对于Lambda表达式支持,学习一下
1.Lambda表达式演进
假定有一个集合对象,现在要实现对于集合对象的过滤操作,过滤条件多变。例如
@Data
public class Dog {
private Integer size;
private String name;
private Integer age;
private Integer price;
public Dog(Integer size, String name, Integer age, Integer price) {
this.size = size;
this.name = name;
this.age = age;
this.price = price;
}
}
集合类
List<Dog> dogs = Arrays.asList(
new Dog(1, "d1", 2, 5),
new Dog(2, "d2", 3, 6),
new Dog(3, "d3", 4, 2),
new Dog(4, "d4", 1, 3)
);
1.筛选出size>1的Dog集合
2.筛选出age>2的Dog集合.。。。
普通实现方式
编写两个方法,遍历集合依次判断,代码如下:
//查询size大于1的Dog
@Test
List<Dog> selectDogsBySize(List<Dog> dogs){
List<Dog> result = new ArrayList<>();
for(Dog dog: dogs){
if(dog.getSize()>1){
result.add(dog);
}
}
return result;
}
//查询age大于2的Dog
@Test
List<Dog> selectDogsByAge(List<Dog> dogs){
List<Dog> result = new ArrayList<>();
for(Dog dog: dogs){
if(dog.getAge()>1){
result.add(dog);
}
}
return result;
}
如果此时过滤条件发生变化,则需要重新编写函数来实现,并且大量函数代码逻辑都是相同的。
策略设计模式
将过滤条件抽离出来,设计成接口
public interface SelectStrategy<T> {
boolean strategy(T t);
}
实现过滤函数逻辑,将选择策略传入
//策略设计模式
//通过某种策略来选择
List<Dog> selectDogsByStrategy(List<Dog> dogs, SelectStrategy strategy){
List<Dog> result = new ArrayList<>();
for(Dog dog: dogs){
if(strategy.strategy(dog)){
result.add(dog);
}
}
return result;
}
对于每一种过滤需求,分别实现一种对应的策略
public class SizeSelectStrategy implements SelectStrategy<Dog> {
@Override
public boolean strategy(Dog dog) {
return dog.getSize() > 2;
}
}
public class AgeSelectStrategy implements SelectStrategy<Dog> {
@Override
public boolean strategy(Dog dog) {
return dog.getSize() > 1;
}
}
将具体策略传入业务逻辑函数,实现不同过滤需求
@Test
public void testStrategy(){
selectDogsByStrategy(dogs, new AgeSelectStrategy());
selectDogsByStrategy(dogs, new SizeSelectStrategy());
}
如果继续有过滤需求,则可以实现不同的过滤策略来实现。假定过滤策略非常多,则会产生很多策略实现类。
匿名内部类
采用匿名内部类方式来实现具体策略
//匿名内部类
@Test
public void testAnonymousInnerClass(){
selectDogsByStrategy(dogs, new SelectStrategy<Dog>() {
@Override
public boolean strategy(Dog o) {
return o.getSize() > 2;
}
});
selectDogsByStrategy(dogs, new SelectStrategy<Dog>() {
@Override
public boolean strategy(Dog o) {
return o.getAge() > 1;
}
});
}
利用匿名内部类可以简化代码,但是匿名内部类大部分代码都是重复无用的。
Lambda表达式
//lambda表达式
@Test
public void testLambda(){
selectDogsByStrategy(dogs, (SelectStrategy<Dog>) o -> o.getAge() > 1);
selectDogsByStrategy(dogs, (SelectStrategy<Dog>) o -> o.getSize() > 2);
}
lambda表达式可以认为是对于匿名内部类简写的语法糖,其本质是实现策略设计模式,简化代码写法。
Stream API
@Test
public void testStream(){
List<Dog> result = dogs.stream().filter(o -> o.getSize() > 2).collect(Collectors.toList());
dogs.stream().filter(o -> o.getSize() > 2).forEach(System.out::println);
dogs.stream().map(Dog::getName).limit(2).forEach(System.out::println);
}
Stream API提供了对于集合的强大编程简化能力。
2.Lambda表达式语法
函数式接口:接口中只有一个抽象方法,可以使用@FunctionalInterface注解来标注
@FunctionalInterface
public interface FunctionInter<T> {
T func(T t);
}
其基本格式如下:
(参数列表) ->{lambda体}
参数列表对应接口中抽象方法的参数,lambda对应抽象方法的逻辑实现。
对于抽象方法的参数以及返回值可以分为以下情况:
1.无参数,无返回值
//抽象方法 无参数 无返回值 ()->
Runnable r2 = () -> System.out.println("lllllllll");
r2.run();
2.一个参数,无返回值
Consumer<String> con = (s) -> System.out.println(s);
con.accept("conconcon");
3.若只有一个参数,左边小括号可以不写
Consumer<String> con2 = s -> System.out.println(s);
con2.accept("conconcon");
4.两个参数,有返回值,lambda体中有多行语句,则需要使用大括号
Comparator<Integer> com = (x, y) -> {
System.out.println("com");
return Integer.compare(x, y);
};
5.lambda体中只有一条语句,则return和大括号可以不写
Comparator<Integer> com2 = (x, y) -> Integer.compare(x, y);
6.lambda表达式参数类型可以不写,JVM编译器可以根据上下文推断出参数类型 类型推断
Comparator<Integer> com3 = (Integer x, Integer y) -> Integer.compare(x, y);
3.Lambda表达式示例
新建一个线程
new Thread(new Runnable() { @Override public void run() { System.out.println("t1"); } }).start(); new Thread(() -> System.out.println("t2")).start();
利用lambda表达式实现策略
@FunctionalInterface public interface FunctionInter<T> { T func(T t); } public Integer doFunc (Integer num, FunctionInter<Integer> functionInter){ return functionInter.func(num); } System.out.println(doFunc(1, x -> x + x)); System.out.println(doFunc(1, x -> x * x)); System.out.println(doFunc(1, x -> x + 100));
排序比较器实现
Collections.sort(dogs, (d1, d2) ->{ if(d1.getName().equals(d2.getName())){ return d1.getName().compareTo(d2.getName()); }else{ return Integer.compare(d1.getSize(), d2.getSize()); } }); dogs.stream().forEach(System.out::println);
4.java8核心函数式接口
//Consumer 消费性接口 //Supplier 供给型接口 //Function<T,R> 函数型接口 //predicate<T> 断言型接口
1.Consumer接口 对应一个参数 无返回值
@FunctionalInterface public interface Consumer<T> { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t);
public void buy(double money, Consumer<Double> con){ con.accept(money); } buy(100, (m) -> System.out.println(m));
2.Supplier接口 无参数 有返回值
@FunctionalInterface public interface Supplier<T> { /** * Gets a result. * * @return a result */ T get(); }
public List<Integer> produce(int num, Supplier<Integer> supplier){ List<Integer> result = new ArrayList<>(); for(int i=0; i<num; i++){ result.add(supplier.get()); } return result; } Random r = new Random(); r.setSeed(new Date().getTime()); produce(10, () -> r.nextInt() * 100).stream().forEach(System.out::println);
3.Function接口 一个参数 一个返回值
@FunctionalInterface public interface Function<T, R> { /** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t);
public String dealString(String str, Function<String, String> function){ return function.apply(str); } dealString("aDcbF", (s) -> s.toLowerCase()); dealString("aDcbF", (s) -> s.toUpperCase());
4.Predicate接口 一个参数 返回true或false
@FunctionalInterface
public interface Predicate<T> {
/**
* Evaluates this predicate on the given argument.
*
* @param t the input argument
* @return {@code true} if the input argument matches the predicate,
* otherwise {@code false}
*/
boolean test(T t);
public List<String> addStringToList(List<String> ll, Predicate<String> predicate){
List<String> result = new ArrayList<>();
for(String s : ll){
if(predicate.test(s)){
result.add(s);
}
}
return result;
}
addStringToList(Arrays.asList("asc","sdcd","dssfdsf"), (s) -> s.startsWith("a"));
5.方法引用与构造器引用
如果lambda体中要实现的方法在已有的类中已经有实现,则可以使用方法引用
要求 函数式接口和已有方法具有相同的参数列表和返回值
其实现形式有 对象::实例方法名 类::静态方法名 类::实例方法名
//对象::实例方法名 Consumer<String> con = (x) -> System.out.println(x); PrintStream ps = System.out; Consumer<String> con2 = ps :: println; Consumer<String> con3 = System.out::println; Dog dog = new Dog(); Supplier<String> supplier = () -> dog.getName(); Supplier<String> supplier1 = dog::getName;
//类::静态方法名 Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y); Comparator<Integer> comparator2 = Integer::compare;
//类::实例方法名 //第一个参数是方法调用者,第二个参数是方法参数 BiPredicate<String, String> biPredicate = (s1, s2) -> s1.equals(s2); BiPredicate<String, String> biPredicate2 = String::equals;
构造器引用
Dog对象
@Data public class Dog { private Integer size; private String name; private Integer age; private Integer price; public Dog(){} public Dog(String name){ this.name = name; } public Dog(Integer size, Integer age){ this.size = size; this.age = age; } public Dog(Integer size, String name, Integer age, Integer price) { this.size = size; this.name = name; this.age = age; this.price = price; } }
//根据参数自动匹配相应的构造器
Function<String, Dog> function = (x) -> new Dog(x); Function<String, Dog> function2 = Dog::new; BiFunction<Integer, Integer, Dog> fun = (x, y) -> new Dog(x, y); BiFunction<Integer, Integer, Dog> fun2 = Dog::new;
数组引用
Function<Integer, String[]> fun3 = (x) -> new String[x]; Function<Integer, String[]> fun4 = String[]::new;