lambda表达式10个示例——学习笔记

摘录:http://www.importnew.com/16436.html

1、lambda实现Runnable

复制代码
// Java 8之前:
new Thread(new Runnable() {
    @Override
    public void run() {
    System.out.println("Before Java8, too much code for too little to do");
    }
}).start();
复制代码
//Java 8方式:用() -> {}代码块替代了整个匿名类
new Thread( () -> System.out.println("In Java8, Lambda expression rocks !!") ).start();

output:

too much code, for too little to do
Lambda expression rocks !!

如果你的方法不对参数进行修改、重写,只是在控制台打印点东西的话,那么可以这样写:

() -> System.out.println("Hello Lambda Expressions");

如果你的方法接收两个参数,那么可以写成如下这样:

如果你的方法接收两个参数,那么可以写成如下这样:

2、使用Java 8 lambda表达式进行事件处理

编写事件监听:

复制代码
// Java 8之前:
JButton show =  new JButton("Show");
show.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
    System.out.println("Event handling without lambda expression is boring");
    }
});
复制代码
// Java 8方式:
show.addActionListener((e) -> {
    System.out.println("Light, Camera, Action !! Lambda expressions Rocks");
});

output:

too much code, for too little to do
Lambda expression rocks !!

3、使用lambda表达式对列表进行迭代

// Java 8之前:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
for (String feature : features) {
    System.out.println(feature);
}
复制代码
// Java 8之后:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API", "Date and Time API");
features.forEach(n -> System.out.println(n));

// 使用Java 8的方法引用更方便,方法引用由::双冒号操作符标示,
// 看起来像C++的作用域解析运算符
features.forEach(System.out::println);

复制代码

output:

Lambdas
Default Method
Stream API
Date and Time API

4、使用lambda表达式和函数式接口Predicate

 java.util.function.Predicate 函数式接口以及lambda表达式,可以向API方法添加逻辑,用更少的代码支持更多的动态行为。

复制代码
public static void main(args[]){
    List languages = Arrays.asList("Java", "Scala", "C++", "Haskell", "Lisp");
System.out.println(</span>"Languages which starts with J :"<span style="color: #000000;">);
filter(languages, <strong><span style="color: #ff0000;">(str)</span></strong></span><strong><span style="color: #ff0000;">-&gt;</span></strong>str.startsWith("J"<span style="color: #000000;">));

System.out.println(</span>"Languages which ends with a "<span style="color: #000000;">);
filter(languages, <span style="color: #ff0000;"><strong>(str)</strong></span></span><span style="color: #ff0000;"><strong>-&gt;</strong></span>str.endsWith("a"<span style="color: #000000;">));

System.out.println(</span>"Print all languages :"<span style="color: #000000;">);
filter(languages, <strong><span style="color: #ff0000;">(str)</span></strong></span><strong><span style="color: #ff0000;">-&gt;</span></strong><span style="color: #0000ff;">true</span><span style="color: #000000;">);

System.out.println(</span>"Print no language : "<span style="color: #000000;">);
filter(languages, <strong><span style="color: #ff0000;">(str)</span></strong></span><strong><span style="color: #ff0000;">-&gt;</span></strong><span style="color: #0000ff;">false</span><span style="color: #000000;">);

System.out.println(</span>"Print language whose length greater than 4:"<span style="color: #000000;">);
filter(languages, <span style="color: #ff0000;"><strong>(str)</strong></span></span><span style="color: #ff0000;"><strong>-&gt;</strong></span>str.length() &gt; 4<span style="color: #000000;">);

}

public static void filter(List names, Predicate condition) {
for(String name: names) {
if(condition.test(name)) {
System.out.println(name
+ " ");
}
}
}

复制代码

output:

复制代码
Languages which starts with J :
Java
Languages which ends with a
Java
Scala
Print all languages :
Java
Scala
C++
Haskell
Lisp
Print no language :
Print language whose length greater than 4:
Scala
Haskell
复制代码

better way:

// 更好的办法
public static void filter(List names, Predicate condition) {
    names.stream().filter((name) -> (condition.test(name))).forEach((name) -> {
        System.out.println(name + " ");
    });
}

5、如何在lambda表达式中加入Predicate

java.util.function.Predicate 允许将两个或更多的 Predicate 合成一个。它提供类似于逻辑操作符AND和OR的方法,名字叫做and()、or()和xor(),用于将传入 filter() 方法的条件合并起来。

复制代码
// 甚至可以用and()、or()和xor()逻辑函数来合并Predicate,
// 例如要找到所有以J开始,长度为四个字母的名字,你可以合并两个Predicate并传入
Predicate<String> startsWithJ = (n) -> n.startsWith("J");
Predicate<String> fourLetterLong = (n) -> n.length() == 4;
names.stream()
    .filter(startsWithJ.and(fourLetterLong))
    .forEach((n) -> System.out.print("nName, which starts with 'J' and four letter long is : " + n));
复制代码

也可以使用 or() 和 xor() 方法

6、Java 8中使用lambda表达式的Map和Reduce示例

将 costBeforeTax 列表的每个元素转换成为税后的值。我们将 x -> x*x lambda表达式传到 map() 方法,后者将其应用到流中的每一个元素。然后用 forEach() 将列表元素打印出来。

复制代码
// 不使用lambda表达式为每个订单加上12%的税
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
for (Integer cost : costBeforeTax) {
    double price = cost + .12*cost;
    System.out.println(price);
}

// 使用lambda表达式
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
costBeforeTax.stream().map((cost)
-> cost + .12*cost).forEach(System.out::println);

复制代码
复制代码
112.0
224.0
336.0
448.0
560.0
112.0
224.0
336.0
448.0
560.0
复制代码

6.2、Java 8中使用lambda表达式的Map和Reduce示例

Map和Reduce操作是函数式编程的核心操作,因为其功能,reduce 又被称为折叠操作。

SQL中类似 sum()、avg() 或者 count() 的聚集函数,实际上就是 reduce 操作,因为它们接收多个值并返回一个值。

流API定义的 reduceh() 函数可以接受lambda表达式,并对所有值进行合并。IntStream这样的类有类似 average()、count()、sum() 的内建方法来做 reduce 操作,也有mapToLong()、mapToDouble() 方法来做转换

复制代码
// 为每个订单加上12%的税
// 老方法:
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double total = 0;
for (Integer cost : costBeforeTax) {
    double price = cost + .12*cost;
    total = total + price;
}
System.out.println("Total : " + total);

// 新方法:可以用内建方法,也可以自己定义
List costBeforeTax = Arrays.asList(100, 200, 300, 400, 500);
double bill = costBeforeTax.stream().map((cost) -> cost + .12*cost).reduce((sum, cost) -> sum + cost).get();
System.out.println(
"Total : " + bill);

复制代码

output:

Total : 1680.0
Total : 1680.0

7、通过过滤创建一个String列表

// 创建一个字符串列表,每个字符串长度大于2
List<String> filtered = strList.stream().filter(x -> x.length()> 2).collect(Collectors.toList());
System.out.printf("Original List : %s, filtered list : %s %n", strList, filtered);

output:

Original List : [abc, , bcd, , defg, jk], filtered list : [abc, bcd, defg]

8、对列表的每个元素应用函数

// 将字符串换成大写并用逗号链接起来
List<String> G7 = Arrays.asList("USA", "Japan", "France", "Germany", "Italy", "U.K.","Canada");
String G7Countries = G7.stream().map(x -> x.toUpperCase()).collect(Collectors.joining(", "));
System.out.println(G7Countries);

9、复制不同的值,创建一个子列表

// 用所有不同的数字创建一个正方形列表
List<Integer> numbers = Arrays.asList(9, 10, 3, 4, 7, 3, 4);
List<Integer> distinct = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
System.out.printf("Original List : %s,  Square Without duplicates : %s %n", numbers, distinct);

output:

Original List : [9, 10, 3, 4, 7, 3, 4],  Square Without duplicates : [81, 100, 9, 16, 49]

10、计算集合元素的最大值、最小值、总和以及平均值

ntStream、LongStream 和 DoubleStream 等流的类中,有个非常有用的方法叫做 summaryStatistics() 。可以返回 IntSummaryStatistics、LongSummaryStatistics 或者 DoubleSummaryStatistic s,描述流中元素的各种摘要数据。

最大值  最小值  所有元素的总和  平均值

复制代码
//获取数字的个数、最小值、最大值、总和以及平均值
List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
System.out.println("Highest prime number in List : " + stats.getMax());
System.out.println("Lowest prime number in List : " + stats.getMin());
System.out.println("Sum of all prime numbers : " + stats.getSum());
System.out.println("Average of all prime numbers : " + stats.getAverage());
复制代码

输出:

Highest prime number in List : 29
Lowest prime number in List : 2
Sum of all prime numbers : 129
Average of all prime numbers : 12.9

Lambda表达式 vs 匿名类

既然lambda表达式即将正式取代Java代码中的匿名内部类,那么有必要对二者做一个比较分析。

一个关键的不同点就是关键字 this:

匿名类的 this 关键字指向匿名类,而lambda表达式的 this 关键字指向包围lambda表达式的类。

另一个不同点是二者的编译方式:

Java编译器将lambda表达式编译成类的私有方法。使用了Java 7的 invokedynamic 字节码指令来动态绑定这个方法

注意:

1)lambda表达式仅能放入如下代码:预定义使用了 @Functional 注释的函数式接口,自带一个抽象函数的方法,或者SAM(Single Abstract Method 单个抽象方法)类型。这些称为lambda表达式的目标类型,可以用作返回类型,或lambda目标代码的参数。例如,若一个方法接收Runnable、Comparable或者 Callable 接口,都有单个抽象方法,可以传入lambda表达式。类似的,如果一个方法接受声明于 java.util.function 包内的接口,例如 Predicate、Function、Consumer 或 Supplier,那么可以向其传lambda表达式。

2)lambda表达式内可以使用方法引用,仅当该方法不修改lambda表达式提供的参数。本例中的lambda表达式可以换为方法引用,因为这仅是一个参数相同的简单方法调用。

list.forEach(n -> System.out.println(n));
list.forEach(System.out::println);  // 使用方法引用
 

然而,若对参数有任何修改,则不能使用方法引用,而需键入完整地lambda表达式,如下所示:

list.forEach((String s) -> System.out.println("*" + s + "*"));

事实上,可以省略这里的lambda参数的类型声明,编译器可以从列表的类属性推测出来。

3)lambda内部可以使用静态、非静态和局部变量,这称为lambda内的变量捕获。

4)Lambda表达式在Java中又称为闭包或匿名函数,所以如果有同事把它叫闭包的时候,不用惊讶。

5)Lambda方法在编译器内部被翻译成私有方法,并派发 invokedynamic 字节码指令来进行调用。可以使用JDK中的 javap 工具来反编译class文件。使用 javap -p 或 javap -c -v 命令来看一看lambda表达式生成的字节码。大致应该长这样:

private static java.lang.Object lambda$0(java.lang.String);

6)lambda表达式有个限制,那就是只能引用 final 或 final 局部变量,这就是说不能在lambda内部修改定义在域外的变量。

List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7});
int factor = 2;
primes.forEach(element -> { factor++; });
Compile time error : "local variables referenced from a lambda expression must be final or effectively final"
 

另外,只是访问它而不作修改是可以的,如下所示:

List<Integer> primes = Arrays.asList(new Integer[]{2, 3,5,7});
int factor = 2;
primes.forEach(element -> { System.out.println(factor*element); })

输出:

4
6
10
14

 

分类: JDK8新特性
标签: JDK8, Java
<div id="blog_post_info">
0
0
<div class="clear"></div>
<div id="post_next_prev">

<a href="https://www.cnblogs.com/L-a-u-r-a/p/8645668.html" class="p_n_p_prefix">« </a> 上一篇:    <a href="https://www.cnblogs.com/L-a-u-r-a/p/8645668.html" title="发布于 2018-03-25 17:43">对象在内存中初始化的过程?</a>
<br>
<a href="https://www.cnblogs.com/L-a-u-r-a/p/9078692.html" class="p_n_p_prefix">» </a> 下一篇:    <a href="https://www.cnblogs.com/L-a-u-r-a/p/9078692.html" title="发布于 2018-05-23 18:45">第1张 Maven简介 学习笔记</a>

Java 8 Lambda表达式和理解

说明:部分资料来源于网络

时间:20190704

Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda 表达式可以使代码变的更加简洁紧凑。

 

一、语法

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

  (左边)输入参数->(右边)lambda主体

(parameters) -> expression;

  或

(parameters) ->{ statements; }

 

2、以下是lambda表达式的重要特征:

(1)输入参数:

  可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。

  可选的参数圆括号:

    a、一个参数无需定义圆括号,但多个参数需要定义圆括号。

    b、如果申明了参数类型,则一定需要圆括号。

(2)lambda主体

  可选的大括号:如果主体包含了一个语句,就不需要使用大括号。

  可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值

 

3、按照上面的格式,lambda不同参数的表达式写法

(1)没有参数的表达式:

() -> System.out.println("this is no parameter Lambda expression");

(2)只有一个参数的表达式:

(x) -> System.out.println("this is only one parameter Lambda expression");

  或者

(X x) -> System.out.println("this is only one parameter Lambda expression");

  和

(x) -> {
    x = x*2;
    System.out.println("this is only one parameter Lambda expression");
    System.out.println("the function is double input value");
    return x;
};

  说明:一个参数的可以不用使用(),如果参数声明了参数类型则必须要加()Lambda主体是语句块的话需要使用{}

(3)有两个或者多个参数的表达式:

(x,y) -> System.out.println("these are several parameters Lambda expression");

  或者

(X x,Y y) -> {
  System.out.println("the function is add two input values");
  System.out.println("these are several parameters Lambda expression");
  return x+y;
};

  说明:有多个参数的lambda表达式,参数使用,隔开。Lambda主体是语句块的话需要使用{}

 

二、Lambda 表达式实例

lambda示例1:

复制代码
/*
 * Predicate<T>接口中boolean test(T t)方法只接收一个参数,返回值为boolean类型,
 * 故lambda表达式的输入参数只有一个参数:x,lambda的主体返回值为boolean类型
 */
Predicate<Integer> atLeast5 = x -> x >= 5;
System.out.println("传入参数是否大于5:" + atLeast5.test(6));

/*

  • BinaryOperator<T>接口中R apply(T t, U u)方法接收两个泛型参数,返回值为也为泛型
  • 故lambda表达式输入Long参数有两个:x,y,lambda的主体返回值为Long类型
    */
    BinaryOperator
    <Long> addLongs = (x, y) -> {
      Long z
    = x + y;
      
    return z;
    };
    System.out.println(
    "计算传入两个Long参数的和:" + addLongs.apply(5l, 6l));

/*

  • Runnable接口中run()方法没有参数,有没有返回值,故lambda表达式没有参数,lambda主体也没有返回值
    */
    Runnable run1
    = () -> System.out.println("这个方法就是run里面的方法");
    run1.run();

try {
  
final Integer value = 9;
  
/
   * 1、Callable<V>接口中V call() throws Exception方法没有参数,故lambda表达式也不能有输入参数,
   * 返回类型为泛型,故lambda主体需要返回指定Integer类型
   * 2、因为.call()方法抛出异常,所以需要抛出或者捕获异常
   * 3、lambda表达式使用外部参数,需要是final类型,及时外部参数没有定义为final类型,
   * 也会隐式的指定为final,故值或者引用地址不能修改。
  
/
  Callable
<Integer> call = () -> value;
  System.out.println(
"无参的方法,所以lambda的参数列表中不能传递参数:" + call.call());
}
catch (Exception e) {
  e.printStackTrace();
}

复制代码

lambda示例二:

复制代码
public class Java8Tester { 

  public static void main(String args[]){

    Java8Tester tester
= new Java8Tester();
    
// 类型声明
    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("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));

    // 不用括号
    GreetingService greetService1 = message -> System.out.println("Hello " + message);
    
// 用括号
    GreetingService greetService2 = (message) -> System.out.println("Hello " + message);

    greetService1.sayMessage("Runoob");
    greetService2.sayMessage(
"Google");
  }

  // 定义接口和接口中为实现的方法
  
interface MathOperation {
    int operation(int a, int b);
  }

  // 定义接口和接口中为实现的方法
  interface GreetingService {
    void sayMessage(String message);
  }

  // 定义方法,方法通过lambda表达式实现了接口中的方法   
private int operate(int a, int b, MathOperation mathOperation){     return mathOperation.operation(a, b);   } }
复制代码

执行以上代码,输出结果为:

10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google

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

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

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

Lambda表达式常用的场景是:函数式接口。函数式接口是指的只有一个抽象方法的接口,我们常用的函数式接口有:

Runnable、Callable、PrivilegedAction、Comparator、FileFilter、PathMatcher、InvocationHandler、PropertyChangeListener、ActionListener、ChangeListener、Function、Predicate、BinaryOperator

 

三、lambda表达式中的变量作用域

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

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

复制代码
public class Java8Tester {

  final static String salutation = "Hello! ";

  public static void main(String args[]){

    GreetingService greetService1
= message -> System.out.println(salutation + message);
    greetService1.sayMessage(
"Runoob");

    //相当于下面==========
    GreetingService g = new GreetingService() {
      @Override
      public void sayMessage(String message) {
        System.out.println(salutation
+ message);
      }
    };
    g.sayMessage(
"jack");

  }

  interface GreetingService {
    
void sayMessage(String message);
  }
}

复制代码

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

$ javac Java8Tester.java 
$ java Java8Tester
Hello! Runoob
Hello! jack

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

复制代码
public class Java8Tester { 

  public static void main(String args[]) {
    
final int num = 1;
    Converter
<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
    s.convert(
2); // 输出结果为 3
  }

  public interface Converter<T1, T2> {
    
void convert(int i);
  }
}

复制代码

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());  //编译会出错

 

posted on 2019-09-26 14:28  张子扬  阅读(897)  评论(0编辑  收藏  举报

导航