暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Java8特性之Lambda 表达式

研发生产率生态 2019-12-28
195

本系列引入Java 8特性和示例。如lambda表达式、Java流、功能接口和日期时间API更改。

  • Lambda Expression


      Lambda表达式,如果你熟悉其他流行编程语言(比如Scala,C#),对Lambda就不感到陌生。在Java编程语言中,Lambda表达式(或函数)只是一个匿名函数。一个没有名称且未绑定到标识符的函数。它们被精确地写在需要的地方,通常作为其他函数的参数。Lambda表达式最重要的特性是它们在其外观的上下文中执行。因此,相同的lambda表达式可以在其他上下文中以不同的方式执行(例如,逻辑是相同的,但结果会根据传递给函数的不同参数而不同)。

       函数式编程相对于面向对象编程(OOP)的好处,大多数OOP语言都是围绕对象和实例发展的,并且只把它们当作自己的第一类公民。另一个重要的实体即功能退居其次。尤其是在java中,函数不能存在于对象之外。在java中,函数本身没有任何意义,除非它与某个对象或实例相关。但是在函数式编程中,您可以定义函数,为它们提供引用变量并将其作为方法参数传递等等。JavaScript是一个很好的例子,您可以在其中传递回调方法,例如 到Ajax调用。这是非常有用的功能,并且从一开始就在Java中缺少。现在使用Java 8,我们也可以使用这些lambda表达式。

Lambda表达式提供的功能:

  • 允许将功能视为方法参数,或将代码视为数据。

  • 可以不属于任何类而创建的函数。

  • lambda表达式可以像对象一样传递并按需执行。

lambda表达式的基本语法为:

   lambda operator -> body

  • 无参数


        () -> System.out.println("无参数");

  •  一个参数

       (p) -> System.out.println(“参数: " + p);

                如果可以从上下文推断出该变量的类型,则不必使用括号

  • 多个参数

         (p1, p2) -> System.out.println(“多个参数: " + p1 + ", " + p2);



  • Lambda Expression的协作规则


  1. lambda表达式可以有零个、一个或多个参数。

  2. 可以显式声明参数的类型,也可以从上下文推断参数的类型。

  3. 多个参数用强制括号括起来,并用逗号分隔。空括号用于表示一组空参数。

  4. 当有单个参数时,如果它的类型是推断出来的,则不强制使用括号。例如a ->返回a*a。

  5.  Lambda表达式的主体可以包含零个,一个或多个语句。

  6.  如果lambda表达式的主体具有单语句,则不强制使用大括号,且匿名函数的返回类型与主体表达式相同。当主体中有多个语句时,必须用大括号括起来。

   以上是Lambda Expression的简单概述,下面会深入介绍Lambda Expression 但在这之前先了解下 functional interfaces。因为这是重要的。

  • Java 8 functional interface


         Lambda表达式就像函数一样,它们也像函数一样接受参数。Lambda表达式基本上表示函数接口的实例

       单一抽象方法接口(SAM接口)不是一个新概念。在Java中,我们已经有许多此类SAM接口的示例。  功能接口是仅包含一个抽象方法的接口。他们只能展示一种功能。从Java 8开始,lambda表达式可用于表示功能接口的实例。功能接口可以具有许多默认方法,通过使用新的注释(即@FunctionalInterface)标记这些接口来实施单一职责规则。Runnable,ActionListener和Comparable是功能接口的一些示例。在Java 8之前,我们必须创建匿名内部类对象或实现这些接口。

例如,Runnable接口的新定义

@FunctionalInterface
public interface Runnable {
public abstract void run();
}

如果想在任何函数接口中添加新方法,编译器将不允许您这样做,并将抛出编译时错误。怎么和Lambda结合呢?我们知道Lambda表达式是不带名称的匿名函数,它们(大多数)作为参数传递给其他函数。在java中,方法参数始终具有类型,并且在确定方法重载甚至是简单方法调用的情况下,都会寻找此类型信息以确定需要调用哪个方法。因此,基本上每个lambda表达式也必须都可以转换为某种类型才能被接受为方法参数。转换lambda表达式的类型始终是函数接口类型。让我们通过一个例子来理解它。

  列如我们创建个线程,那么最简单的代码将是:

public class FgMainTest {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("创建一个线程");
}
}).start();
}
}

 如果我们使用lambda表达式执行此任务,则代码为:

public class FgMainLambdaTest {
public static void main(String[] args) {
new Thread(()->{
System.out.println("lambda:创建一个线程");
}).start();
}
}

  Runnable是一个具有单个方法run()的函数接口。因此,当您将lambda表达式传递给Thread类的构造函数时,编译器将尝试将该表达式转换为等效的可运行代码,如第一个代码示例所示。如果编译器成功,那么一切运行良好,如果编译器不能将表达式转换为等效的实现代码,它将会报错。在上面的例子中,lambda表达式被转换为Runnable类型。

附上Thread构造函数:

public Thread(Runnable target) {
this(null, target, "Thread-" + nextThreadNum(), 0);
}
  • lambda 表达式例子

         我列出了一些代码例子,你可以阅读和分析一个lambda表达式如何在日常编程中使用。

  • 遍历一个列表并执行一些操作

import java.util.ArrayList;
import java.util.List;

public class FgMainLambdaTest_example_1 {
public static void main(String[] args) {
// 创建List集合
{1, 2, 3, 4}
List<Integer> arrL = new ArrayList<Integer>();
arrL.add(1);
arrL.add(2);
arrL.add(3);
arrL.add(4);
// 用lambda expression 打印集合arrL中所有元素
arrL.forEach(n -> System.out.println(n));
arrL.forEach(n -> {
if (n % 2 == 0) System.out.println(n);
});
}
}


  • 排序

import java.util.Arrays;
import java.util.List;

public class FgMainLambdaTest_example_2 {

public static void main(String[] ar) {

List<Department> departmentList = Arrays.asList(
new Department("研发部"),
new Department("测试部"),
new Department("运营部"),
new Department("框架部")
);

System.out.println("排序前: " + Arrays.toString(departmentList.toArray()));

departmentList.sort(Department::nameCompare);
System.out.println("排序后 " + Arrays.toString(departmentList.toArray()));
/*
输出结果:
排序前: [研发部, 测试部, 运营部, 框架部]
排序后 [框架部, 测试部, 研发部, 运营部]
*/
}
}

class Department {
String name;

Department(String name) {
this.name = name;
}


public static int nameCompare(Department a1, Department a2) {
/**
* java string compareTo()方法按字母顺序比较给定字符串和当前字符串。它返回正数,负数或0。
* 它根据字符串中每个字符的Unicode值来比较字符串。
* 如果第一个字符串在词法上大于第二个字符串,则返回正数(字符值之差)。
* 如果第一个字符串在词法上小于第二个字符串,则返回负数;如果第一个字符串在词法上等于第二个字符串,则返回0。
*/
return a1.name.compareTo(a2.name);
}

public String toString() {
return name;
}
}

http://39.99.159.233 技术博客实时





文章转载自研发生产率生态,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论