Java8之Lambda表达式

匿名内部类

在介绍如何使用 Lambda 表达式之前,我们先来看看匿名内部类,例如,我们使用匿名内部类比较两个 Integer 类型数据的大小

Comparator<Integer>    com    =    new    Comparator<Integer>()    { 
@Override
public    int    compare(Integer   o1,   Integer    o2)    { 
return   Integer.compare(o1,    o2);
} 
};

在上述代码中,我们使用匿名内部类实现了比较两个 Integer 类型数据的大小。
接下来,我们就可以将上述匿名内部类的实例作为参数,传递到其他方法中了,如下所示。
TreeSet treeSet = new TreeSet<>(com);
完整的代码如下所示:

    public static void main(String[] args) {
        Comparator<Integer> comparator  = new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
//                return Integer.compare(o1,o2);
                return o2-o1;
            }
        };
        TreeSet<Integer> integers = new TreeSet<>(comparator);
        integers.addAll(Arrays.asList(1,6,5,3,7,8,2));
        System.out.println(integers);
    }   

在这里插入图片描述
我们分析下上述代码,在整个匿名内部类中,实际上真正有用的就是下面一行代码。

return    Integer.compare(o1,   o2); 或 return o2-o1;

Lambda表达式

如果使用 Lambda 表达式完成两个 Integer 类型数据的比较,我们该如何实现呢?
Comparator com = (x, y) -> Integer.compare(x, y);
我们也可以将 Lambda 表达式传递到 TreeSet 的构造方法中,如下所示。
TreeSet treeSet = new TreeSet<>((x, y) -> Integer.compare(x, y));
直观的感受就是使用 Lambda 表达式一行代码就能搞定匿名内部类多行代码的功能,非常简洁。

对比常规方法和Lambda表达式

常规模式

如有以下数据模型:

@Data
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class Emp {

    private String username;

    private Integer age;

    private Double salary;

}

当进行不同属性比较时,会有如下操作:

List<Emp> emps = Arrays.asList(new Emp("zhangsan", 15, 5000.0),
                new Emp("lisi", 16, 4000.0),
                new Emp("wangwu", 17, 6000.0));
        emps.forEach(emp -> {
            if(emp.getAge()>16){
                System.out.println(emp);
            }
        });
        emps.forEach(emp -> {
            if(emp.getSalary()>5000){
                System.out.println(emp);
            }
        });

如果此时我们再来一个需求,查找当前公司中年龄小于或者等于 20 的员工信息,那我们又要单独写一个过滤了。使用常规方法是真的不方便啊!

使用设计模式优化代码

先定义一个过滤器接口类:

public interface MyPredicate<T>{
    public boolean filter(T t);
}

实现过滤器接口类:

public class FilterEmployeeByAge implements MyPredicate<Emp>{
    @Override
    public boolean filter(Emp emp) {
        return emp.getAge()>16;
    }
}

测试:

List<Emp> emps = Arrays.asList(new Emp("zhangsan", 15, 5000.0),
                new Emp("lisi", 16, 4000.0),
                new Emp("wangwu", 17, 6000.0));
        MyPredicate myPredicate = new FilterEmployeeByAge();
        emps.forEach(emp -> {
            if(myPredicate.filter(emp)){
                System.out.println(emp);
            }
 });

此时,你会发现,当你将遍历打印封装成方法时,如果你需要以其他方式过滤,只需要实现MyPredicate接口,并引用对应的实现即可,这就是设计模式的魅力。
使用设计模式优化代码也有不好的地方:每次定义一个过滤策略的时候,我们都要单独创建一 个过滤类!

匿名内部类

 List<Emp> emps = Arrays.asList(new Emp("zhangsan", 15, 5000.0),
                new Emp("lisi", 16, 4000.0),
                new Emp("wangwu", 17, 6000.0));
        MyPredicate<Emp> myPredicate = new MyPredicate<Emp>() {
            @Override
            public boolean filter(Emp emp) {
                if(emp.getSalary()>5000.0)
                    return true;
                return false;
            }
        };
        emps.forEach(emp -> {
            if(myPredicate.filter(emp)){
                System.out.println(emp);
            }
 });

匿名内部类看起来比常规遍历集合的方法要简单些,并且将使用设计模式优化代码时,每次创
建一个类来实现过滤规则写到了匿名内部类中,使得代码进一步简化了。

Lambda表达式

List<Emp> emps = Arrays.asList(new Emp("zhangsan", 15, 5000.0),
               new Emp("lisi", 16, 4000.0),
               new Emp("wangwu", 17, 6000.0));
       emps.stream().filter(emp -> emp.getAge()>16).forEach(emp-> System.out.println(emp));
       emps.stream().filter(emp -> emp.getSalary()>6000.0).forEach(emp -> System.out.println(emp));

使用 Lambda 表达式结合 Stream API,只要给出相应的集合,我们就可以完成对集合的各种过滤并输出结果信息。
只给出一个集合,使用 Lambda 表达式和 Stream API,一行代码就能够过滤出想要的元素并进行输出。

匿名类到Lambda表达式

Runnable runnable = new Runnable() {
           @Override
           public void run() {
               System.out.println("test");
           }
       };
runnable = ()-> System.out.println("hello");

从直观上看,Lambda 表达式要比常规的语法简洁的多。

Lambda表达式到语法

Lambda 表达式在 Java 语言中引入了 “->” 操作符, “->” 操作符被称为 Lambda 表达式
的操作符或者箭头操作符,它将 Lambda 表达式分为两部分:

  • 左侧部分指定了 Lambda 表达式需要的所有参数。(Lambda 表达式本质上是对接口的实现,Lambda 表达式的参数列表本质上对应着接口中方法的参数列表)
  • 右侧部分指定了 Lambda 体,即 Lambda 表达式要执行的功能。(Lambda 体本质上就是接口方法具体实现的功能。)
语法格式一:无参,无返回值,Lambda 体只有一条语句

Runnable r = () -> System.out.println(“Hello Lambda”);

语法格式二:Lambda 表达式需要一个参数,并且无返回值

Consumer func = (s) -> System.out.println(s);

语法格式三:Lambda 只需要一个参数时,参数的小括号可以省略

Consumer func = s -> System.out.println(s);

语法格式四:Lambda 需要两个参数,并且有返回值
BinaryOperator<Integer>    bo    =    (a,   b)   ->    { 
System.out.println("函数式接口"); 
return   a    +    b;
};
Comparator<Integer>    comparator    =    (x,    y)   ->    { 
System.out.println("函数式接口");
return   Integer.compare(x,   y); 
};
语法格式五:当 Lambda 体只有一条语句时,return 和大括号可以省略

BinaryOperator bo = (a, b) -> a + b;
Comparator comparator = (x, y) -> Integer.compare(x, y);

语法格式六:Lambda 表达式的参数列表的数据类型可以省略不写,因为 JVM 编译器能够通过上下文推断出数据类型,这就是“类型推断”
BinaryOperator<Integer>    bo    =    (Integer   a,    Integer   b)    ->    { 
return   a    +    b;
};

等价于

BinaryOperator<Integer>    bo    =    (a,   b)   ->    { 
return   a    +    b;
};

上述 Lambda 表达式中的参数类型都是由编译器推断得出的。Lambda 表达式中无需指定类型,
程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的“类型推断”。

函数式接口

1)、Lambda 表达式需要函数式接口的支持,只包含一个抽象方法的接口,称为函数式接口。
2)、可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
3)、可以在任意函数式接口上使用@FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
4)、下面自定义函数式接口,并使用 Lambda 表达式来实现相应的功能。
例如,使用函数式接口和 Lambda 表达式实现对字符串的处理功能。
首先,我们定义一个函数式接口 MyFunc,如下所示。

@FunctionalInterface
public interface MyFunc <T>{
    public T getValue(T t);
}

接下来,我们定义一个操作字符串的方法,其中参数为 MyFunc 接口实例和需要转换的字符串。

public static String handlerString(MyFunc<String> myFunc,String str){
        return myFunc.getValue(str); 
}

接下来,我们对自定义的函数式接口进行测试,此时我们传递的函数式接口的参数为 Lambda表达式,并且将字符串转化为大写。
String hello = handlerString(s -> s.toUpperCase(), “hello”);
System.out.printf(hello);
运行结果如下:在这里插入图片描述
我们也可以截取字符串的某一部分,如下所示。
String hello = handlerString(s -> s.substring(2),“hello”);
可以看到, 我们可以通过handlerString (MyFunc myFunc,String str)方法结合Lambda 表达式对字符串进行任意操作。
注意:作为参数传递Lambda表达式:为了将Lambda表达式作为参数传递,接收 Lambda表达式的参数类型必须是与该Lambda表达式兼容的函数式接口的类型。

Lambda表达式案例

案例一
List<Emp> emps = Arrays.asList(new Emp("zhangsan", 18, 5000.0),
                new Emp("lisi", 16, 4000.0),
                new Emp("wangwu", 17, 6000.0));
List<Emp> list = emps.stream().sorted((o1, o2) -> o1.getAge() - o2.getAge()).collect(Collectors.toList());
list.stream().forEach(emp -> System.out.println(emp));
案例二
String hello = handlerString(s -> s.toUpperCase(), "hello");
String substr = handlerString(s -> s.substring(2),"hello");
案例三
@FunctionalInterface
public interface Func <T,R>{
    R getValue(T t1,T t2);
}
private static Integer handTwoArgs(int i, int i1, Func<Integer,Integer> func) {
   return func.getValue(i,i1);
}
handTwoArgs(1,2,(x,y)->x+y);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逆天至尊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值