Java 8 - 02 Lambda Expression

简介: Java 8 - 02 Lambda Expression

20200510181139786.png


Pre


上一节 Java 8 - 01 优雅编程 lambda 以及 @FunctionalInterface注解一点通

中有的时候使用了匿名类来表示不同的行为 ,代码比较啰嗦

  List targetEngineerList6 = enginnerTest.findEnginner(enginnerList, new EnginnerFilter() {
            @Override
            public boolean getMatchedEnginner(Enginner enginner) {
                return "Python".equals(enginner.getJob());
            }
        });

Java 8中如何解决这个问题呢?

Lambda表达式 。 它可以让你很简洁地表示一个行为或传递代码。现在你可以把Lambda表达式看作匿名功能,它基本上就是没有声明名称的方法,但和匿名类一样,它也可以作为参数传递给一个方法。


Lambda 初探


可以把Lambda表达式理解为简洁地表示可传递的匿名函数的一种方式:它没有名称,但它有参数列表、函数主体、返回类型,可能还有一个可以抛出的异常列表。


匿名——我们说匿名,是因为它不像普通的方法那样有一个明确的名称

函数—— Lambda函数不像方法那样属于某个特定的类。但和方法一样,Lambda有参数列表、函数主体、返回类型,还可能有可以抛出的异常列表。

传递——Lambda表达式可以作为参数传递给方法或存储在变量中。

简洁——无需像匿名类那样写很多模板代码。


我们来看下lambda的语法

( o1,  o2) -> o1.getJob().compareTo(o2.getJob());


  • ( o1, o2) : 参数 ,1个参数的话,可以省略括号
  • -> : 箭头,固定写法
  • o1.getJob().compareTo(o2.getJob()) : lambda主题

我们看看使用lambda之前的语法


     Comparator<Enginner> enginnerComparator = new Comparator<Enginner>() {
            @Override
            public int compare(Enginner o1, Enginner o2) {
                return o1.getJob().compareTo(o2.getJob());
            }
        };

用了Lambda表达式:

 Comparator<Enginner> enginnerComparator = ( o1,  o2) -> o1.getJob().compareTo(o2.getJob());


所以说:

  • 参数列表——这里它采用了 Comparator 中 compare 方法的参数,两个 Enginner
  • 箭头——箭头 -> 把参数列表与Lambda主体分隔开
  • Lambda主体——比较两个 Enginner 的职位。表达式就是Lambda的返回值


为啥这里连return也没有了呢? 如果 你加了 {} , 就必须要带上return了

( o1, o2) -> { return o1.getJob().compareTo(o2.getJob()) ;}如果没有 { } 就一行代码,其实是可以省略的,lambda中是可以省略的


为了更进一步的理解lambda,我们接着看几个常见的lambda表达式

Function<String,Integer> flambda = (String s) -> s.length();


第一个Lambda表达式具有一个 String 类型的参数并返回一个 int 。Lambda没有 return 语句,因为已经隐含了 return 。


这个语句的功能,输入一个字符串,返回字符串的长度 。 如果你需要定义一个Lambda,将输入对象的信息映射 到输出 , java.util.function.Function<T, 接口 是你的不二选择


Predicate<Enginner>  predicate= (Enginner e) -> e.getAge() > 30 ;


第二个Lambda 表达式有一个 Enginner类 型的参数并返回一 个 boolean (Enginner 的年龄是否大于30)

在你需要表示一个涉及类型 T 的布尔表达式时,就可以使用java.util.function.Predicate<T>这个接口

   (int x,int y ) -> {
            System.out.println(x);
            System.out.println(y);
        };

第三个Lambda表达式具有两个 int 类型的参数而没有返回值( void 返回)。注意Lambda表达式可以包含多行语句,这里是两行.


() -> 18


第四个Lambda 表达式没有参数,返回一个 int


Comparator<Enginner> comparator2 = ( o1,  o2) -> o1.getJob().compareTo(o2.getJob());



第五个Lambda表达式具有两个 Enginner类型的参数,返回一个 int :比较两个 Enginner的职业。


lambda语法


Lambda 的基本语法是

(parameters) -> expression


或(请注意语句的花括号)

(parameters) -> { statements; }


小测验一把

根据上述语法规则,以下哪个不是有效的Lambda表达式?

(1)  () -> {}
(2)  () -> "Raoul"
(3)  () -> {return "Mario";}
(4)  (Integer i) -> return "Alan" + i;
(5)  (String s) -> {"IronMan";}


答案:只有4和5是无效的Lambda。

(1) 这个Lambda没有参数,并返回 void 。它类似于主体为空的方法: public void run() {} 。

(2) 这个Lambda没有参数,并返回 String 作为表达式。

(3) 这个Lambda没有参数,并返回 String (利用显式返回语句)。

(4) return 是一个控制流语句。要使此Lambda有效,需要使花括号,如下所示: (Integer i) -> {return "Alan" + i;} 。 还得有 分号

(5)“Iron Man”是一个表达式,不是一个语句。要使此Lambda有效,你可以去除花括和分号,如下所示: (String s) -> "Iron Man" 。或者如果你喜欢,可以使用显式返回语句,如下所示: (String s)->{return "IronMan";} 。


再举几个例子 ,加深下印象:

布尔表达式

(List<String> list) -> list.isEmpty()


创建对象

() -> new Apple(10)


消费一个对象

(Enginner a) -> {
    System.out.println(a.getAge());
}


从一个对象中选择/抽取

(String s) -> s.length()


组合两个值

(int a, int b) -> a * b


比较两个对象

Comparator<Enginner> comparator2 = ( o1,  o2) -> o1.getJob().compareTo(o2.getJob());


函数式接口

enginnerTest.findEnginner(enginnerList,engineer -> {
            return "Java".equals(engineer.getJob());
        });


就可以优化为

enginnerTest.findEnginner(enginnerList,engineer ->  "Java".equals(engineer.getJob()) );


那到底在哪里可以使用Lambda呢?你可以在函数式接口上使用Lambda表达式。在上面的代

码中,我们把 Lambda表达式作为第二个参数传给 filter 方法,因为它这里需要EnginnerFilter ,而这是一个函数式接口.

20200513003401680.png

EnginnerFilter就是一个标准的函数式接口。 因为 EnginnerFilter 仅仅定义了一个抽象方法getMatchedEnginner.


20200513003438706.png

一言以蔽之,函数式接口就是只定义一个抽象方法的接口

实际上 接口现在还可以拥有 默认方法(即在类没有对方法进行实现时,其主体为方法提供默认实现的方法)。哪怕有很多默认方法,只要接口只定义了一个抽象方法抽象方法,它就仍然是一个函数式接口

测试下

下面哪些接口是函数式接口?

public interface Adder{
  int add(int a, int b);
}
public interface SmartAdder extends Adder{
  int add(double a, double b);
}
public interface Nothing{
}

只有 Adder 是函数式接口。

SmartAdder 不是函数式接口,因为它定义了两个叫作 add 的抽象方法(其中一个是从

Adder 那里继承来的)。

Nothing 也不是函数式接口,因为它没有声明抽象方法

用函数式接口可以干什么呢?Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例(具体说来,是函数式接口一个具体实现的实例)。


当然了,你用匿名内部类也可以完成同样的事情,只不过比较笨拙:需要提供一个实现,然后再直接内联将它实例化。


举个例子

  // 使用lambda
  Runnable r1 = () -> System.out.println("Hello artisan Lambda");
    // 使用匿名内部类
    Runnable r2 = new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello artisan Inner");
        }
    };
    r1.run();
    r2.run();


函数描述符


函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。我们将这种抽象方法叫作函数描述符。

举个例子


Runnable 接口可以看作一个什么也不接受什么也不返回( void )的函数的签名,因为它只有一个叫作 run 的抽象方法,这个方法什么也不接受,什么也不返回( void )。

(Enginner o1, Enginner o2) -> o1.getJob().compareTo(o2.getJob());

代表接受两个 Enginner 作为参数且返回 int 的函数。


Lambda表达式可以被赋给一个变量,或传递给一个接受函数式接口作为参数的方法 ,当然这个Lambda表达式的签名要和函数式接口的抽象方法一样


比如 你可以像下面这样直接把一个Lambda传给 process 方法:

public void process(Runnable r){
  r.run();
}
process(() -> System.out.println("This is Great!!"));

不接受参数且返回 void 。 这恰恰是 Runnable 接口中 run 方法的签名。

小测一把

以下哪些是使用Lambda表达式的有效方式?


(1) execute(() -> {});
  public void execute(Runnable r){
  r.run();
}
(2) public Callable<String> fetch() {
  return () -> "Tricky example ;-)";
}
(3) Predicate<Apple> p = (Apple a) -> a.getWeight();


答案:只有1和2是有效的。


第一个例子有效,是因为Lambda () -> {} 具有签名 () -> void ,这和 Runnable 中的抽象方法 run 的签名相匹配。请注意,此代码运行后什么都不会做,因为Lambda是空的


第二个例子也是有效的。事实上, fetch 方法的返回类型是 Callable<String> 。

Callable<String> 基本上就定义了一个方法,签名是 () -> String ,其中 T 被 String 代替

了。因为Lambda () -> "Trickyexample;-)" 的签名是 () -> String ,所以在这个上下文

中可以使用Lambda。


第三个例子无效,因为Lambda表达式 (Apple a) -> a.getWeight() 的签名是

(Apple) -> Integer ,这和 Predicate<Apple>:(Apple) -> boolean 中定义的 test 方法的签名不同。

相关文章
|
17天前
|
Java API 开发者
Java中的Lambda表达式与Stream API的协同作用
在本文中,我们将探讨Java 8引入的Lambda表达式和Stream API如何改变我们处理集合和数组的方式。Lambda表达式提供了一种简洁的方法来表达代码块,而Stream API则允许我们对数据流进行高级操作,如过滤、映射和归约。通过结合使用这两种技术,我们可以以声明式的方式编写更简洁、更易于理解和维护的代码。本文将介绍Lambda表达式和Stream API的基本概念,并通过示例展示它们在实际项目中的应用。
|
19天前
|
Java API 开发者
Java中的Lambda表达式:简洁代码的利器####
本文探讨了Java中Lambda表达式的概念、用途及其在简化代码和提高开发效率方面的显著作用。通过具体实例,展示了Lambda表达式如何在Java 8及更高版本中替代传统的匿名内部类,使代码更加简洁易读。文章还简要介绍了Lambda表达式的语法和常见用法,帮助开发者更好地理解和应用这一强大的工具。 ####
|
21天前
|
并行计算 Java 编译器
深入理解Java中的Lambda表达式
在Java 8中引入的Lambda表达式,不仅简化了代码编写,还提升了代码可读性。本文将带你探索Lambda表达式背后的逻辑与原理,通过实例展示如何高效利用这一特性优化你的程序。
|
26天前
|
搜索推荐 Java API
探索Java中的Lambda表达式
本文将深入探讨Java 8引入的Lambda表达式,这一特性极大地简化了代码编写,提高了程序的可读性。通过实例分析,我们将了解Lambda表达式的基本概念、使用场景以及如何优雅地重构传统代码。文章不仅适合初学者,也能帮助有经验的开发者加深对Lambda表达式的理解。
|
1月前
|
Java
探索Java中的Lambda表达式
【10月更文挑战第37天】本文将带你深入理解Java的Lambda表达式,从基础语法到高级特性,通过实例讲解其在函数式编程中的应用。我们还将探讨Lambda表达式如何简化代码、提高开发效率,并讨论其在实际项目中的应用。
|
1月前
|
Java API
Java中的Lambda表达式与函数式编程####
【10月更文挑战第29天】 本文将深入探讨Java中Lambda表达式的实现及其在函数式编程中的应用。通过对比传统方法,我们将揭示Lambda如何简化代码、提高可读性和维护性。文章还将展示一些实际案例,帮助读者更好地理解和应用Lambda表达式。 ####
|
1月前
|
Java API 开发者
Java中的Lambda表达式与函数式编程####
在Java的演变过程中,Lambda表达式和函数式编程的引入无疑是一次重大的飞跃。本文将深入探讨Lambda表达式的定义、用法及优势,并结合实例说明如何在Java中利用Lambda表达式进行函数式编程。通过对比传统编程方式,揭示Lambda表达式如何简化代码、提高开发效率和可维护性。 ####
|
16天前
|
安全 Java API
Java中的Lambda表达式:简化代码的现代魔法
在Java 8的发布中,Lambda表达式的引入无疑是一场编程范式的革命。它不仅让代码变得更加简洁,还使得函数式编程在Java中成为可能。本文将深入探讨Lambda表达式如何改变我们编写和维护Java代码的方式,以及它是如何提升我们编码效率的。
|
19天前
|
安全 Java API
Java中的Lambda表达式与Stream API的高效结合####
探索Java编程中Lambda表达式与Stream API如何携手并进,提升数据处理效率,实现代码简洁性与功能性的双重飞跃。 ####
24 0
|
1月前
|
Java API 数据处理
探索Java中的Lambda表达式与Stream API
【10月更文挑战第22天】 在Java编程中,Lambda表达式和Stream API是两个强大的功能,它们极大地简化了代码的编写和提高了开发效率。本文将深入探讨这两个概念的基本用法、优势以及在实际项目中的应用案例,帮助读者更好地理解和运用这些现代Java特性。