一文看懂 Java8 的 Lambda表达式!

IT领域的技术日新月异,Java14很快就要问世了,但是对于国内的许多程序员来说,连Java8都还没有真正掌握。

今天,我们就来温习一下Java8的特性之一,Lambda表达式。

Lambda表达式的前世--匿名类

以往,使用单一抽象方法的接口被用作函数类型。它们的实例表示函数(functions)或行动(actions)。自从 JDK 1.1 于 1997 年发布以来,创建函数对象的主要手段就是匿名类。

匿名类,通俗地讲,就是没有类名,直接通过new关键字创建这个类的实例。下面是匿名类的一个例子:

java.util包中的Comparator接口

public interface Comparator<T> {
  int compare(T o1, T o2);
}

使用匿名类创建排序的比较方法(强制排序顺序):

Collections.sort(words, new Comparator<String>() {
  public int compare(String s1, String s2) {
      return Integer.compare(s1.length(), s2.length());
 }
});

匿名类适用于需要函数对象的经典面向对象设计模式,特别是策略模式,上面的匿名类是排序字符串的具体策略。然而,匿名类确实过于冗长。

Lambda表达式的今生

在 Java 8 中,语言形式化了这样的概念,即使用单个抽象方法的接口是特别的,应该得到特别的对待。这些接口现在称为函数式接口,并且该语言允许你使用lambda 表达式或简称 lambda 来创建这些接口的实例。Lambdas 在功能上与匿名类相似,但更为简洁。下面的代码使用 lambdas 替换上面的匿名类。清晰明了

Collections.sort(words,(s1, s2) -> Integer.compare(s1.length(), s2.length()));

你告诉我还没理解Lambda表达式?

那我们再来一个简单的例子:

首先我们定义一个Learn接口

@FunctionalInterface
public interface Learn{
  void study();
}

为了获取 Learn接口的实现对象,可以为该接口定义一个实现类 StudyDemo

public class StudyDemo implements Learn{
  @Override    
  public void study() {    
    System.out.println("好好学习,天天向上");        
  }    
}

然后创建该实现类的对象调用study方法:

  Learn s = new StudyDemo ();        
  s.study();//运行结果-->好好学习,天天向上

如果StudyDemo 类只是为了实现 Learn接口而存在的,且仅被使用了一次,所以使用匿名内部类来简化这一操作:

public static void main(String[] args) {    
  studyInterface s = new studyInterface () {        
  @Override            
  public void study() {            
    System.out.println("好好学习,天天向上");                
    }
  }; 
  s.study();//运行结果-->好好学习,天天向上       
  }    

显然,使用匿名内部类还不够简洁,所以我们用lambda表达式来优化:

因为Learn接口中只有一个方法,编译器使用称为类型推断的过程从上下文中推导出这些类型和方法,所以我们可以省去study的方法名和new Learn,并加上箭头 ->

public static void main(String[] args) {    
  Learn s = ()->{        
    System.out.println("好好学习,天天向上");                
    }
  }; 
  s.study();//运行结果-->好好学习,天天向上       
  }    

如果像上面一样lambda表达式只有一行代码,我们可以进一步优化:

public static void main(String[] args) {    
  Learn s = ()-> System.out.println("好好学习,天天向上");           
  s.study(); //运行结果-->好好学习,天天向上      
  }    

 一行代码对于 lambda 说是理想的,三行代码是合理的最大值。如果违反这一规定,可能会严重损害程序的可读性。如果一个 lambda 很长或很难阅读,要么找到一种方法来简化它或重构你的程序来消除它。

 

至此为止,相信你已经对lambda有所了解,我们再来几个例子加深理解

上面我们举的例子中,study()方法既没有参数也没有返回值,如果有参数和返回值又该怎么办呢?

首先定义一个接口中的唯一抽象方法带参数的情况:

interface Learn1{
    void study(int a,int b);
} 

再看看它的匿名内部类和lambda表达式写法:

public static void main(String[] args) {
  //先定义一个接口的引用
  Learn1 learn;
       //匿名内部类写法
    learn = new Learn1() {
        @Override
        public void study(int a,int b) {
            System.out.println("好好学习x"(a+b));
        }
    };
    learn.study(1,2);//运行结果-->好好学习x2


    //lambda表达式写法,省略接口和方法名,
    //方法的参数类型可以推导出来,也可以省略
    learn = (a,b)->{
        System.out.println("好好学习x"(a+b));
    };
    learn.study(3,4);//运行结果-->好好学习x12

如果方法中只有一个参数,还可以省略小括号,Lambda表达式中只有一条语句可以省略大括号

 learn = e-> System.out.println("好好学习"+e);
  learn.study(5);

  

再来看看有返回值的情况,再回过头来看java.util包中的Comparator接口:

public interface Comparator<T> {
  int compare(T o1, T o2);
}

先定义一个学生类:

public class Student {
    private String name;
    private int age;

    public Student(String ,int age){
      this.name = name;
      this.age = age;
    }
    public int getAge(){
      return this.age;
}

接下来我们对数组中的Student对象,使用Arrays的sort方法通过年龄进行升序排序

public static void main(String[] args) {
  Student[] array = {
         new Student("张三", 18),    
         new Student("李四", 20),    
         new Student("王五", 19) };
   //匿名内部类写法
    Comparator<Student > compare = new Comparator<Student >() {
            @Override
            public int compare(Student s1, Student s2) {
                return s1.getAge() ‐ s2.getAge();
            }
        };
        //调用
            Arrays.sort(array, compare); 


    //lambda表达式写法,去掉接口名和方法名
    Comparator<Student > compare = (Student s1, Student s2)->{
                return s1.getAge() ‐ s2.getAge();
            }
        };
        //调用
        Arrays.sort(array, compare); 


    //省略写法,去掉参数类型,只保留返回值
     Comparator<Student > compare = (s1,s2)-> s1.getAge() ‐ s2.getAge();

总结

Lambda表达式的语法非常简洁,但是使用时有几个问题需要特别注意:

1. 使用Lambda表达式必须具有接口,且要求接口中有且仅有一个抽象方法。

2. 使用Lambda必须具有上下文推断。也就是方法的参数或局部变量类型必须为Lambda对应的接口类型,才能使用Lambda作为该接口的实例。

 Lambda标准形式

 (参数类型 参数名称) ‐> { 代码语句 }

 说明:

1. 小括号内:没有参数就留空(),多个参数就用逗号分隔。

2. -> 是新引入的语法格式,代表指向动作。

3. 大括号内的语法与传统方法体要求基本一致。


Lambda的省略:凡是可以根据上下文推导得知的信息,都可以省略

在Lambda表达式标准形式的基础上:

1. 小括号内参数的类型可以省略;

2. 如果小括号内只有一个参数,则小括号可以省略;

3. 如果大括号内只有一个语句,则无论是否有返回值,都可以省略大括号、return关键字及语句分号。

综上所述,从 Java 8 开始,lambda 是迄今为止表示小函数对象的最佳方式。除非必须创建非函数式接口类型的实例,否则不要使用匿名类作为函数对象。

备注:有且仅有一个抽象方法的接口,称为“函数式接口”。

1. 全栈架构之打包推荐【建议收藏,常读】

2. 一个空格引发的“惨案“

3. 分布式系统中Session共享的常用方案

4Java语言“坑爹”排行榜TOP 10

5. 我是一个Java类(附带精彩吐槽)

6. mysql索引失效,差点我的工作凉了

7. 既生synchronized,何生volatile?

8. 微服务一直火,为什么服务化要搞懂?

9. MySQL的COUNT语句,不简单!

10. 漫画:HashSet和TreeSet实现与原理

扫码二维码关注我

·end·

—如果本文有帮助,请分享到朋友圈吧—

我们一起愉快的玩耍!

你点的每个赞,我都认真当成了喜欢

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值