java8新特性——Lambda 表达式总结

文章目录

    • Lambda 学习总结
      • 引言
      • Lambda 表达式介绍
        • 1 Lambda 简介
        • 2 Lambda 作用
        • 3 接口要求
        • 4 @FunctionalInterface 注解作用
      • Lambda 表达式语法
        • 1 语法结构
        • 2 Lambda 表达式的重要特征
        • 3 Lambda 典型案例
      • 三、 Lambda 表达式入门案例
        • 1 定义函数接口
        • 2 实现函数接口
        • 3 Lambda 语法简化
      • 四、Lambda 表达式的使用
        • 1 Lambda 表达式引用方法
          • 1.1语法
          • 1.2 案例
        • 2 Lambda 表达式创建线程
        • 3 操作集合
        • 4 Lambda 表达式中的闭包问题
          • 4.1 什么是闭包

Lambda 学习总结

引言

Lambda 表达式是 JDK8 的一个新特性,可以取代大部分的匿名内部类,写出更优雅的代码。最近学到Disruptor时,使用其publishEvent方法中的lambda表达式,发现对于这块理解的还不是很透彻,在此重新梳理一下。

Lambda 表达式介绍

1 Lambda 简介

在 Java 语言中,可以为变量赋予一个值。

把一个代码块赋给一变量,在 Java 8 之前,是做不到的。但是 Java 8 问世之后,利用 Lambda 特性,就可以做到了。

我们来看一个例子,这个例子中我们看到removeIf的参数是一个Predicate类型的对象,里面有一个未实现的方法test(T t)。按照java 8之前我们要new 一个继承Predicate的对象,然后传给removeIf。

而这里ele -> ele.equals(“b”)就相当于创建了一个Predicate对象,并实现了test方法。变量filter被赋值了一个代码块ele -> ele.equals(“b”)。“这块代码”,或者说 “这个被赋给一个变量的函数”,就是一个 Lambda 表达式。

 List<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.removeIf(ele -> ele.equals("b")); //使用了lambda表达式

// removeIf源码
default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) { //调用接口中的test方法
                each.remove();
                removed = true;
            }
        }
        return removed;
    }

// Predicate 接口
public interface Predicate<T> {
    
    // 未实现接口
    boolean test(T t);
    
     // default方法
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    // default方法
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

    // default方法
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
} 

2 Lambda 作用

最直观的作用就是使得代码变得异常简洁。

3 接口要求

虽然使用 Lambda 表达式可以对某些接口进行简单的实现,但并不是所有的接口都可以使用 Lambda 表达式来实现。Lambda 规定接口中只能有一个需要被实现的方法,不是规定接口中只能有一个方法。

jdk 8 中有另一个新特性:default, 被 default 修饰的方法会有默认实现,不是必须被实现的方法(从上面的代码中也能看到),所以不影响 Lambda 表达式的使用。

4 @FunctionalInterface 注解作用

@FunctionalInterface 标记在接口上,“函数式接口”是指仅仅只包含一个抽象方法的接口。

Lambda 表达式语法

1 语法结构

(parameters) -> expression

(parameters) ->{ statements; }

语法形式为 () -> {},其中 () 用来描述参数列表,{} 用来描述方法体,-> 为 lambda 运算符 ,读作(goes to)。

2 Lambda 表达式的重要特征

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

可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。

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

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

3 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) 

// 6. 大括号需要指明返回结果

(x, y) -> {
x = y;
return x – y 
}

三、 Lambda 表达式入门案例

下面的案例是lambda最复杂的一种形式了。其他可以根据上面的案例,自行试验比如无参无返,有参无返等等。

1 定义函数接口

/**
 *有返回值,有多个参数* 
*/

@FunctionalInterface 

interface ReturnMultiParam{ 

int method(int a,int b); 

} 

2 实现函数接口

public static void main(String[] args) { 

/***
*有返回值,有多个参数* 
*/

ReturnMultiParam returnMultiParam = (int a ,int b)->{ 

System.out.print("ReturnMultiParam "); 

return a+b; 

};

System.out.println(returnMultiParam.method(10,20)); 

}

3 Lambda 语法简化

/***

*有返回值,有多个参数* 

*/

ReturnMultiParam returnMultiParam = (int a ,int b)->{

System.out.print("ReturnMultiParam "); 

return a+b; 

};

/***

* 简化版

*/

ReturnMultiParam returnMultiParam = (a ,b)->a+b; 

System.out.println(returnMultiParam.method(10,20)); 

}

四、Lambda 表达式的使用

1 Lambda 表达式引用方法

有时候我们不是必须使用 Lambda 的函数体定义实现,我们可以利用 lambda 表达式指向一个已经被实现的方法。

1.1语法

方法归属者::方法名 静态方法的归属者为类名,普通方法归属者为对象。

1.2 案例
/*** 有返回值,有一个参数 */ 
@FunctionalInterface interface ReturnOneParam{ 
    int method(int a); 
}

public class Test { 

ReturnMultiParam returnMultiParam = (a ,b)->a+b; 

System.out.println(returnMultiParam.method(10,20)); 

}

/*** 要求: 
* 1,参数的个数以及类型需要与函数接口中的抽象方法一致。 
* 2,返回值类型要与函数接口中的抽象方法的返回值类型一致。 
* @param a * @return 
*/
public static int doubleNum(int a){ 
return 2*a; 

}

public int addTwo(int a){ 

return a+2; 

} 

}


public class Test2 { 

public static void main(String[] args) { 

ReturnOneParam returnOneParam = Test::doubleNum; 

int value = returnOneParam.method(10); 

System.out.println(value); 

Test test = new Test(); 

ReturnOneParam returnOneParam1 = test::addTwo; 

int value2 = returnOneParam1.method(10); 

System.out.println(value2); 

} 

}

2 Lambda 表达式创建线程

public class Test3 { 

public static void main(String[] args) { 

System.out.println(Thread.currentThread().getName()+ "开始"); 

new Thread(()->{ 

for(int i=0;i<20;i++){ 

try {

Thread.sleep(500); 

} catch (InterruptedException e) { 

e.printStackTrace(); 

} 

System.out.println(Thread.currentThread().getName()+" "+i); 

} 

},"Lambda Thread ").start(); 

System.out.println(Thread.*currentThread*().getName()+"结束"); 

} 

}

3 操作集合

我们可以调用集合的 public void forEach(Consumer action) 方法,通过 lambda 表达式的方式遍历集合中的元素。Consumer 接口是 jdk 为我们提供的一个函数式接口。

我们通过 public boolean removeIf(Predicate filter)方法来删除集合中的某个元素,Predicate 也是 jdk 为我们提供的一个函数式接口。

传入比较器重写compare 方法的比较器对象,现在我们还可以使用lambda表达式来简化代码。

以下是代码示例

public class Test4 { 

public static void main(String[] args) { 

List<String> list = new ArrayList<>(); 

list.add("a"); 

list.add("b"); 

list.add("c"); 

list.add("d"); 

// 遍历打印
list.forEach(System.out::println); 
// 排序
list.sort((o1,o2)->o1.compareTo(o2)); 
list.forEach(System.out::println);
// 删除某种元素
list.removeIf(ele->ele.equals("b")); 
list.forEach(System.out::println); 
} 

} 

4 Lambda 表达式中的闭包问题

4.1 什么是闭包

闭包的本质就是代码片断。所以闭包可以理解成一个代码片断的引用。在 Java 中匿名内部类也是闭包的一种实现方式。

在闭包中访问外部的变量时,外部变量必须是 final 类型,虚拟机会帮我们加上 final 修饰关键字。

public class Test7 { 

public static void main(String[] args) {

int num =10; 

NoReturnNoParam noReturnNoParam = 

()->System.out.println(num); 

noReturnNoParam.method(); 

} 

} 

你可能感兴趣的:(java,Lambda,java,开发语言)