菜鸟教程 -- 学的不仅是技术,更是梦想!

Java 教程
(追記) (追記ここまで)

Java Lambda 表达式

Java 8 新特性 Java 8 新特性


Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

使用 Lambda 表达式可以使代码变的更加简洁紧凑。

语法

lambda 表达式的语法格式如下:

(parameters) -> expression(parameters) ->{statements; }

parameters 是参数列表,expression{ statements; } 是Lambda 表达式的主体。如果只有一个参数,可以省略括号;如果没有参数,也需要空括号。

下面是一个简单的例子,展示了使用 Lambda 表达式计算两个数的和:

实例

// 使用 Lambda 表达式计算两个数的和
MathOperation addition = (a, b) -> a + b;

// 调用 Lambda 表达式
int result = addition.operation(5, 3);
System.out.println("5 + 3 = " + result);

在上面的例子中,MathOperation 是一个函数式接口,它包含一个抽象方法 operation,Lambda 表达式 (a, b) -> a + b 实现了这个抽象方法,表示对两个参数进行相加操作。

Lambda 表达式可以用于各种场景,包括集合操作、事件处理等,下面是一个使用 Lambda 表达式对列表进行遍历的例子:

实例

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

// 使用 Lambda 表达式遍历列表
names.forEach(name -> System.out.println(name));

Lambda 表达式在 Java 中引入了更函数式编程的风格,使得代码更加简洁和易读,它是 Java 8 中对函数式编程的一次重要改进。


重要特征

简洁性

Lambda 表达式提供了一种更为简洁的语法,尤其适用于函数式接口。相比于传统的匿名内部类,Lambda 表达式使得代码更为紧凑,减少了样板代码的编写。

实例

// 传统的匿名内部类
Runnable runnable1 = new Runnable() {
@Override
public void run() {
System.out.println("Hello World!");
}
};

// 使用 Lambda 表达式
Runnable runnable2 = () -> System.out.println("Hello World!");

函数式编程支持

Lambda 表达式是函数式编程的一种体现,它允许将函数当作参数传递给方法,或者将函数作为返回值,这种支持使得 Java 在函数式编程方面更为灵活,能够更好地处理集合操作、并行计算等任务。

实例

// 使用 Lambda 表达式作为参数传递给方法
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(name -> System.out.println(name));

变量捕获

Lambda 表达式可以访问外部作用域的变量,这种特性称为变量捕获,Lambda 表达式可以隐式地捕获 final 或事实上是 final 的局部变量。

实例

// 变量捕获
int x = 10;
MyFunction myFunction = y -> System.out.println(x + y);
myFunction.doSomething(5); // 输出 15

方法引用

Lambda 表达式可以通过方法引用进一步简化,方法引用允许你直接引用现有类或对象的方法,而不用编写冗余的代码。

实例

// 使用方法引用
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach(System.out::println);

可并行性

Lambda 表达式能够更方便地实现并行操作,通过使用 Stream API 结合 Lambda 表达式,可以更容易地实现并行计算,提高程序性能。

实例

// 使用 Lambda 表达式和 Stream API 进行并行计算
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream().mapToInt(Integer::intValue).sum();

Lambda 表达式的引入使得 Java 编程更加灵活、简洁,并推动了函数式编程的发展。


Lambda 表达式实例

Lambda 表达式的简单例子:

// 1. 不需要参数,返回值为 5 
() -> 5 
 
// 2. 接收一个参数(数字类型),返回其2倍的值 
x -> 2 * x 
 
// 3. 接受2个参数(数字),并返回他们的差值 
(x, y) -> x – y 
 
// 4. 接收2个int型整数,返回他们的和 
(int x, int y) -> x + y 
 
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void) 
(String s) -> System.out.print(s)

在 Java8Tester.java 文件输入以下代码:

Java8Tester.java 文件

publicclassJava8Tester{publicstaticvoidmain(Stringargs[]){Java8Testertester = newJava8Tester(); // 类型声明MathOperationaddition = (inta, intb) -> a + b; // 不用类型声明MathOperationsubtraction = (a, b) -> a - b; // 大括号中的返回语句MathOperationmultiplication = (inta, intb) -> {returna * b; }; // 没有大括号及返回语句MathOperationdivision = (inta, intb) -> a / b; System.out.println("10 + 5 = " + tester.operate(10, 5, addition)); System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction)); System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication)); System.out.println("10 / 5 = " + tester.operate(10, 5, division)); // 不用括号GreetingServicegreetService1 = message -> System.out.println("Hello " + message); // 用括号GreetingServicegreetService2 = (message) -> System.out.println("Hello " + message); greetService1.sayMessage("Runoob"); greetService2.sayMessage("Google"); }interfaceMathOperation{intoperation(inta, intb); }interfaceGreetingService{voidsayMessage(Stringmessage); }privateintoperate(inta, intb, MathOperationmathOperation){returnmathOperation.operation(a, b); }}

执行以上脚本,输出结果为:

$ javac Java8Tester.java 
$ java Java8Tester
10 +たす 5 = 15
10 -ひく 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google

使用 Lambda 表达式需要注意以下两点:

  • Lambda 表达式主要用来定义行内执行的方法类型接口(例如,一个简单方法接口)。在上面例子中,我们使用各种类型的 Lambda 表达式来定义 MathOperation 接口的方法,然后我们定义了 operation 的执行。

  • Lambda 表达式免去了使用匿名方法的麻烦,并且给予 Java 简单但是强大的函数化的编程能力。


变量作用域

lambda 表达式只能引用标记了 final 的外层局部变量,这就是说不能在 lambda 内部修改定义在域外的局部变量,否则会编译错误。

在 Java8Tester.java 文件输入以下代码:

Java8Tester.java 文件

publicclassJava8Tester{finalstaticStringsalutation = "Hello! "; publicstaticvoidmain(Stringargs[]){GreetingServicegreetService1 = message -> System.out.println(salutation + message); greetService1.sayMessage("Runoob"); }interfaceGreetingService{voidsayMessage(Stringmessage); }}

执行以上脚本,输出结果为:

$ javac Java8Tester.java 
$ java Java8Tester
Hello! Runoob

我们也可以直接在 lambda 表达式中访问外层的局部变量:

Java8Tester.java 文件

publicclassJava8Tester{publicstaticvoidmain(Stringargs[]){finalintnum = 1; Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num)); s.convert(2); // 输出结果为 3}publicinterfaceConverter<T1, T2> {voidconvert(inti); }}

lambda 表达式的局部变量可以不用声明为 final,但是必须不可被后面的代码修改(即隐性的具有 final 的语义)

int num = 1; 
Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
s.convert(2);
num = 5; 
//报错信息:Local variable num defined in an enclosing scope must be final or effectively 
 final

在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量。

String first = ""; 
Comparator<String> comparator = (first, second) -> Integer.compare(first.length(), second.length()); //编译会出错 

Java 8 新特性 Java 8 新特性

AI 思考中...

7 篇笔记 写笔记

  1. #0

    117***[email protected]

    36
    System.out.println("10 + 5 = " + tester.operate(10, 5, addition));

    这里还有另外一种写法

    System.out.println("10+5=" + addition.operation(10, 5));
    interface MathOperation { int operation(int a, int b); }

    此接口要求必须是函数式接口,如果其中有两个方法则lambda表达式会编译错误。但java8的新特性如许实现如下写法:

    interface MathOperation {
     int operation(int a, int b);
     default int addition(int a, int b){
     return a+b;
     }
    }

    117***[email protected]

    9年前 (2017年12月02日)
  2. #0

    lambdas 实现 Runnable 接口

    下面是使用 lambdas 来实现 Runnable 接口的示例:

    // 1.1使用匿名内部类 
    new Thread(new Runnable() { 
     @Override 
     public void run() { 
     System.out.println("Hello world !"); 
     } 
    }).start(); 
     
    // 1.2使用 lambda expression 
    new Thread(() -> System.out.println("Hello world !")).start(); 
     
    // 2.1使用匿名内部类 
    Runnable race1 = new Runnable() { 
     @Override 
     public void run() { 
     System.out.println("Hello world !"); 
     } 
    }; 
     
    // 2.2使用 lambda expression 
    Runnable race2 = () -> System.out.println("Hello world !"); 
     
    // 直接调用 run 方法(没开新线程哦!) 
    race1.run(); 
    race2.run(); 

    Runnable 的 lambda 表达式,使用块格式,将五行代码转换成单行语句。 接下来,在下一节中我们将使用 lambdas 对集合进行排序。

    使用 Lambdas 排序集合

    在 Java 中,Comparator 类被用来排序集合。 在下面的例子中,我们将根据球员的 name

    surnamename 长度以及最后一个字母。 和前面的示例一样,先使用匿名内部类来排序,然后再使用 lambda 表达式精简我们的代码。

    在第一个例子中,我们将根据name来排序list。 使用旧的方式,代码如下所示:

    String[] players = {"Rafael Nadal", "Novak Djokovic", 
     "Stanislas Wawrinka", "David Ferrer", 
     "Roger Federer", "Andy Murray", 
     "Tomas Berdych", "Juan Martin Del Potro", 
     "Richard Gasquet", "John Isner"}; 
     
    // 1.1 使用匿名内部类根据 name 排序 players 
    Arrays.sort(players, new Comparator<String>() { 
     @Override 
     public int compare(String s1, String s2) { 
     return (s1.compareTo(s2)); 
     } 
    }); 

    使用 lambdas,可以通过下面的代码实现同样的功能:

    // 1.2 使用 lambda expression 排序 players 
    Comparator<String> sortByName = (String s1, String s2) -> (s1.compareTo(s2)); 
    Arrays.sort(players, sortByName); 
     
    // 1.3 也可以采用如下形式: 
    Arrays.sort(players, (String s1, String s2) -> (s1.compareTo(s2))); 
    其他的排序如下所示。 和上面的示例一样,代码分别通过匿名内部类和一些lambda表达式来实现Comparator :
    // 1.1 使用匿名内部类根据 surname 排序 players 
    Arrays.sort(players, new Comparator<String>() { 
     @Override 
     public int compare(String s1, String s2) { 
     return (s1.substring(s1.indexOf(" ")).compareTo(s2.substring(s2.indexOf(" ")))); 
     } 
    }); 
     
    // 1.2 使用 lambda expression 排序,根据 surname 
    Comparator<String> sortBySurname = (String s1, String s2) -> 
     ( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) ); 
    Arrays.sort(players, sortBySurname); 
     
    // 1.3 或者这样,怀疑原作者是不是想错了,括号好多... 
    Arrays.sort(players, (String s1, String s2) -> 
     ( s1.substring(s1.indexOf(" ")).compareTo( s2.substring(s2.indexOf(" ")) ) ) 
     ); 
     
    // 2.1 使用匿名内部类根据 name lenght 排序 players 
    Arrays.sort(players, new Comparator<String>() { 
     @Override 
     public int compare(String s1, String s2) { 
     return (s1.length() - s2.length()); 
     } 
    }); 
     
    // 2.2 使用 lambda expression 排序,根据 name lenght 
    Comparator<String> sortByNameLenght = (String s1, String s2) -> (s1.length() - s2.length()); 
    Arrays.sort(players, sortByNameLenght); 
     
    // 2.3 or this 
    Arrays.sort(players, (String s1, String s2) -> (s1.length() - s2.length())); 
     
    // 3.1 使用匿名内部类排序 players, 根据最后一个字母 
    Arrays.sort(players, new Comparator<String>() { 
     @Override 
     public int compare(String s1, String s2) { 
     return (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)); 
     } 
    }); 
     
    // 3.2 使用 lambda expression 排序,根据最后一个字母 
    Comparator<String> sortByLastLetter = 
     (String s1, String s2) -> 
     (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1)); 
    Arrays.sort(players, sortByLastLetter); 
     
    // 3.3 or this 
    Arrays.sort(players, (String s1, String s2) -> (s1.charAt(s1.length() - 1) - s2.charAt(s2.length() - 1))); 
    8年前 (2018年09月03日)
  3. #0

    天命风流

    lic***[email protected]

    9

    参考实例:

    public class Test2 {
     public static void main(String[] args) {
     Test2 tester = new Test2();
     tester.testOperation();
     }
     public void testOperation() {
     /*
     * 
     * 1.不需要参数,返回值为5
     * () -> 5
     * 
     * 2.接收一个参数(数字类型),返回其2倍的值
     * x -> 2 * x
     * 
     * 3.接收2个参数(数字类型),返回他们的差值
     * (x,y) -> x - y
     * 
     * 4.接收2个int型整数,返回他们的和
     * (int x, int y) -> x + y
     * 
     * 5.接收一个String对象,并在控制台打印,不返回任何值(返回void)
     * (String s) -> System.out.print(s)
     * 
     * 6.接收2个参数(数字类型),第1个数字加1,第2个数字加2,再求积
     * (int x, int y) -> { x += 1; y += 2; return x * y }
     * 
     */
     /*
     * 
     * MathOperation接口有且只有一个抽象方法operation时,
     * 可以使用 MathOperation addition = (int a, int b) -> a + b;
     * addition为实现了接口的一个对象,
     * (int a, int b) -> a + b;作为一个函数实现了MathOperation的抽象方法operation;
     * 当MathOperation接口存在两个抽象方法时;
     * MathOperation addition = (int a, int b) -> a + b;在编写时报错;
     * 
     */
     // 传入的参数带有类型声明
     MathOperation addition = (int a, int b) -> a + b;
     // 不用带类型声明
     MathOperation subtraction = (a, b) -> a = b;
     // 大括号中带返回语句
     MathOperation multiplication = (int a, int b) -> {
     return a * b;
     };
     // 没有带大括号及返回语句
     MathOperation division = (int a, int b) -> a / b;
     System.out.println("2 * 3 = " + operate(2, 3, multiplication));
     System.out.println("6 / 3 = " + operate(6, 3, (int a, int b) -> a / b));
     // 输出结果,无返回值
     GreetingService greetService = (String message) ->
     System.out.println("hello " + message);
     greetService.sayMessage("world");
     }
     interface MathOperation {
     int operation(int a, int b);
     // int operation2(int a, int b);
     default int operation3(int a, int b) {
     return a + b;
     }
     }
     interface GreetingService {
     void sayMessage(String message);
     }
     private int operate(int a, int b, MathOperation mathOperation) {
     return mathOperation.operation(a, b);
     }
    }

    天命风流

    lic***[email protected]

    8年前 (2018年12月05日)
  4. #0

    使用 Lambda 表达式对中文排序:

    import java.text.Collator;
    import java.util.Arrays;
    import java.util.List;
    import java.util.Locale;
    public class Test {
     public static void main(String[] args) { 
     List<String> list = Arrays.asList("谷歌", "腾讯", "百度", "淘宝"); 
     Collator collator = Collator.getInstance(Locale.CHINA); 
     list.sort((string1, string2) -> collator.compare(string1, string2)); 
     System.out.println(list);
     }
    }
    7年前 (2019年05月17日)
  5. #0

    奋斗的小猿

    157***[email protected]

    7

    以下实例反映出 lambda 不一定需要 final 关键字才可以访问外部变量。

    public class LambdaTest {
     final static String hello = "Hello, ";
     private static String goodbye = "Goodbye, ";
     public static void main(String[] args) {
     GreetingMessage message1 = (String message) ->
     System.out.println(hello + message);
     GreetingMessage message2 = (message) ->
     {
     //goodbye = "the time is late, I'd go now, goodbye";
     goodbye += "the time is late, I'd go now, ";
     System.out.println(goodbye + message);
     };
     
     message1.sayMessage("张三");
     message2.sayMessage("李四");
     }
     
     interface GreetingMessage {
     void sayMessage(String message);
     }
    }

    运行程序后,结果 :

    Hello, 张三
    Goodbye, the time is late, I'd go now, 李四

    奋斗的小猿

    157***[email protected]

    7年前 (2019年07月24日)
  6. #0

    ByVie

    635***[email protected]

    44

    Lambda

    匿名内部类的格式:

    new 父类或接口() {​
     重写的方法;​ 
    }

    在匿名内部类中,有很多内容都是冗余的。

    比如在使用匿名内部类实现多线程的代码中。

    • 因为 Thread 构造方法中需要传递 Runnable 接口类型的参数,所以我们不得不 new Runnable。
    • 因为要重写 Runnable 中的 run 方法,所以不得不写了public void run。

    整个匿名内部类中最关键的东西是方法,方法中最关键的有前中后三点。

    • 前:参数。
    • 中:方法体
    • 后:返回值

    最好的情况是只关注匿名内部类中最核心的这些内容(方法参数,方法体,返回值)如果使用Lambda表达式,可以只关注最核心的内容,Lambda 表达式是匿名内部类的简化写法。

    Lambda 属于函数式编程思想。

    • 面向对象思想:怎么做。
    • 函数式编程思想:做什么。
    public class Demo01Inner {
     public static void main(String[] args) {
     //使用匿名内部类的方式实现多线程。
     new Thread(new Runnable() {
     @Override
     public void run() {
     System.out.println(Thread.currentThread().getName() + "执行了");
     }
     }).start();
     //使用Lambda表达式实现多线程
     new Thread(() -> System.out.println(Thread.currentThread().getName() + "执行了")).start();
     }
    }

    匿名内部类可以省去单独创建 .java 文件的操作。但是匿名内部类也是有缺点的,写法太冗余了,里面有很多多余的部分。

    匿名内部类也有简化写法,匿名内部类的简化写法是 Lambda 表达式匿名内部类中最关键的内容是方法的参数,方法体,以及返回值,而在 Lambda 表达式中,关注的就是这三个关键的东西。

    Lambda 表达式的标准格式:

    (参数类型 参数名) -> {
     方法体;
     return 返回值;
    }

    格式解释:

    • 小括号中的参数和之前方法的参数写法一样,可以写任意个参数,如果多个参数,要使用逗号隔开。
    • -> 是一个运算符,表示指向性动作。
    • 大括号中的方法体以及 return 返回值的写法和之前方法的大括号中的写法一样。

    Lambda 表达式是函数式编程思想。

    函数式编程:可推导,就是可省略。

    • 因为在 Thread 构造方法中需要 Runnable 类型的参数,所以可以省略 new Runnable。
    • 因为 Runnable 中的只有一个抽象方法 run,所以重写的必然是这个 run 方法,所以可以省略 run 方法的声明部分(public void run)

    Lambda 表达式可以省略面向对象中的一些条条框框,让我们只关注最核心的内容。

    public class Demo02Lambda {
     public static void main(String[] args) {
     //实现多线程(单独创建.java)
     new Thread(new Task()).start();
     //使用匿名内部类的方式实现多线程
     new Thread(new Runnable() {
     @Override
     public void run() {
     System.out.println(Thread.currentThread().getName() + "执行了");
     }
     }).start();
     //使用Lambda表达式完成多线程
     new Thread(() -> {
     System.out.println(Thread.currentThread().getName() + "执行了");
     }).start();
     }
    }

    匿名内部类与 Lambda 函数比较

    public class Demo03Collections {
     public static void main(String[] args) {
     //创建集合
     List<Student> list = new ArrayList<>();
     //添加元素
     list.add(new Student("嫐", 20));
     list.add(new Student("嬲", 18));
     list.add(new Student("挊", 22));
     //使用比较器排序对集合中的学生对象根据年龄升序排序
     //Collections.sort(list, new Rule());
     //使用匿名内部类
     Collections.sort(list, new Comparator<Student>() {
     @Override
     public int compare(Student o1, Student o2) {
     return o1.getAge() - o2.getAge();
     }
     });
     
     //使用Lambda表达式
     Collections.sort(list, (Student o1, Student o2) -> {
     return o1.getAge() - o2.getAge();
     });
     Collections.sort(list, (o1, o2) -> o1.getAge() - o2.getAge());
     System.out.println(list);
     }
    }

    Lambda 格式

    Lambda 表达式的标准格式:

    (参数类型 参数名) -> {
     方法体;
     return 返回值;
    }

    Lambda 表达式的省略规则:

    • 1. 小括号中的参数类型可以省略。
    • 2. 如果小括号中只有一个参数,那么可以省略小括号。
    • 3. 如果大括号中只有一条语句,那么可以省略大括号,return,分号。
    public class Demo04SimpleLambda {
     //定义方法,使用接口当做参数
     public static void method(MyInterface m) {
     m.printStr("hello");
     }
     public static void main(String[] args) {
     //调用method方法,参数传递MyInterface实现类对象
     method(new MyInterface() {
     @Override
     public void printStr(String str) {
     System.out.println(str);
     }
     });
     //使用Lambda表达式的标准格式。
     method((String str) -> {
     System.out.println(str);
     });
     //1. 小括号中的参数类型可以省略。
     method((str) -> {
     System.out.println(str);
     });
     //2. 如果小括号中只有一个参数,那么可以省略小括号。
     method(str -> {
     System.out.println(str);
     });
     //3. 如果大括号中只有一条语句,那么可以省略大括号,return,分号。
     method(str -> System.out.println(str));
     }
    }

    Lambda 使用条件

    Lambda 表达式的使用前提:

    • 必须有接口(不能是抽象类),接口中有且仅有一个需要被重写的抽象方法。
    • 必须支持上下文推导,要能够推导出来 Lambda 表达式表示的是哪个接口中的内容。

    可以使用接口当做参数,然后传递 Lambda 表达式(常用),也可以将 Lambda 表达式赋值给一个接口类型的变量。

    public class Demo05BeforeLambda {
     //使用接口当做参数
     public static void method(MyInterface m) {//m = s -> System.out.println(s)
     m.printStr("HELLO");
     }
     public static void main(String[] args) {
     //使用接口当做参数,然后传递Lambda表达式。
     //method(s -> System.out.println(s));
     //使用匿名内部类方式创建对象
     /*
     MyInterface m = new MyInterface() {
     @Override
     public void printStr(String str) {
     System.out.println(str);
     }
     };
     */
     MyInterface m = str -> System.out.println(str);
     m.printStr("Hello");
     }
    }

    ByVie

    635***[email protected]

    7年前 (2019年08月17日)
  7. #0

    穹的小叮当

    wdl***[email protected]

    6

    测试实例:

    public class LambdaDemo {
     final static String ST = "Hello!";
     String ss = "Hello!";
     public static void main(String[] args) {
     int c = 8;
     int d = 4;
     // int a = 10; 不允许声明一个与局部变量同名的参数或者局部变量。
     AddService add = (a, b) -> a + b;
     System.out.println(c + " + " + d + " = " + add.operation(c, d));
     add = (a, b) -> a - b;
     System.out.println(c + " - " + d + " = " + add.operation(c, d));
     add = (a, b) -> a * b;
     System.out.println(c + " * " + d + " = " + add.operation(c, d));
     add = (a, b) -> a / b;
     System.out.println(c + " / " + d + " = " + add.operation(c, d));
     add = (int a, int b) -> {
     return a + b;
     };
     System.out.println(c + " + " + d + " = " + add.operation(c, d));
     // 打印方法中可以获取ST,c,d 但是获取不到ss (且c,d不可更改,隐性的具有 final 的语义)
     GreetingService gs = str -> System.out.println(ST + str);
     gs.operation(String.valueOf(c));
     }
     /**
     * 实现方法可自定义
     */
     interface AddService {
     Integer operation(int a, int b);
     }
     interface GreetingService {
     void operation(String str);
     }
    }

    穹的小叮当

    wdl***[email protected]

    7年前 (2019年09月28日)

点我分享笔记

  • 昵称 (必填)
  • 邮箱 (必填)
  • 引用地址

AltStyle によって変換されたページ (->オリジナル) /