Java8新特性_Lambda表达式

  最近实在是闲的蛋疼, 突然想起前一段时间使用Lambda表达式觉得惊为天人, 所以就去仔细的学习了一下, 整理出一份博客出来供大家观赏.

一. 什么是lambda表达式.

  Lambda 是一个匿名函数,可以把 Lambda表达式 理解为是一段可以传递的代码 (将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升.

   上面的概念什么意思呢?我们来看一个例子:

   很久以前我们实现runnable的代码如下:

Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello");
            }
        };

  如果使用lambda表达式的话, 代码就会变成下面这个样子:

Runnable runnable = () -> System.out.println("Hello");

  情况很明显, 使用表达式(由于lambda实在是难打, 文章后面都使用表达式代表)可以减少很多代码, 代码也会变得很简洁.但是,表达式写出的代码比较难维护, 我们只能悄咪咪的使用.为什么?因为可以装逼.

二.Lambda 表达式的基础语法

左侧 : Lambda 表达式的参数列表
右侧 : Lambda 表达式中所需执行的功能, 即 Lambda 体

语法格式一 : 无参数,无返回值
() -> System.out.println("Hello Lambda!");

语法格式二 : 有一个参数,并且无返回值
(x) -> System.out.println(x)

语法格式三 : 若只有一个参数,小括号可以省略不写
x -> System.out.println(x)

语法格式四 : 有两个以上的参数,有返回值,并且 Lambda 体中有多条语句
Comparator<Integer> com = (x, y) -> {
    System.out.println("函数式接口");
    return Integer.compare(x, y);
};
语法格式五 : 若 Lambda 体中只有一条语句,
return 和 大括号都可以省略不写 Comparator<Integer> com = (x, y) -> Integer.compare(x, y); 语法格式六 : Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断” (Integer x, Integer y) -> Integer.compare(x, y);

注 : Lambda 表达式中的参数类型都是由编译器推断得出的。 Lambda 表达式中无需指定类型,程序依然可以编译,这是因为 javac 根据程序的上下文,在后台推断出了参数的类型。 Lambda 表达式的类型依赖于上下文环境,是由编译器推断出来的。这就是所谓的 “类型推断”

三.Java8 内置的四大核心函数式接口

  函数接口是只有一个抽象方法的接口,用作 Lambda 表达式的类型。

表达式这四种类型分别代表四种类型的方法, 可在后面进行方法引用.熟悉这个对之后调用带有function的方法有奇效.每种类型的接口还有兄弟姐妹, 感兴趣可以在eclipse中Ctrl+T了解一下;

可能看完这个表格不是特别理解, 我们挑一个来解释, 其他的就举一反三.

比如最常用得function, 当我们需要使用的时候可以看到, 它是需要传入两个泛型的T, R. T是参数类型, 也就是apply方法里面的参数,包括你在表达式内容里做的处理的对象类型.R是返回对象的类型.

举个例子, Function<String, Integer> flambda = s -> s.length(); s.length()是放在apply方法里用的, 因为只有一句话所以不用写return.当然也可以写成 s->{return s.length();};

四.方法引用

若 Lambda 体中的功能,已经有方法提供实现,可以使用方法引用 (可以将方法引用理解为 Lambda 表达式的另外一种表现形式)
1> 对象的引用 :: 实例方法名
2> 类名 :: 静态方法名
3> 类名 :: 实例方法名
注 : 1> 方法引用所引用的方法的参数列表与返回值类型,需要与函数式接口中抽象方法的参数列表和返回值类型保持一致(就是函数签名和返回值一致)
       2> 若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式 : ClassName::MethodName
通俗来讲就是
方法引用首先是建立在熟悉前面几种lambda表达式的情况下才去使用的;

方法无非几种类型

1. 有参数传入(存在2个以上需要自己设计function接口), 有参数返回-->R set(T t)

2. 有参数传入,
无参数返回-->void

3. 无参数传入有参数返回-->get();

4. boolean 判断-->当然也可以用function进行转换
也就是针对不同的方法类型, 使用不同的lambda表达式;

详细的例子如下:

PrintStream ps = System. out;
Consumer<String> con1 = (str) -> ps.println(str);
Consumer<String> con2 = ps::println;
Consumer<String> con3 = System. out::println;
 
对象的引用 :: 实例方法名
Employee emp = new Employee(101, "张三", 18, 9999.99);
Supplier<String> sup = () -> emp.getName();
System. out.println(sup.get());
Supplier<String> sup2 = emp::getName;
System. out.println(sup2.get());
 
类名 :: 静态方法名
Comparator<Integer> comparator1 = (x, y) -> Integer. compare(x, y);
Comparator<Integer> comparator2 = Integer:: compare;
 
BiFunction<Double, Double, Double> fun = (x, y) -> Math. max(x, y);
System. out.println(fun.apply(1.5, 22.2));
BiFunction<Double, Double, Double> fun2 = Math:: max;
System. out.println(fun2.apply(1.2, 1.5));
 
类名 :: 实例方法名
BiPredicate<String, String> bp = (x, y) -> x.equals(y);
System. out.println(bp.test("abcde", "abcde"));
BiPredicate<String, String> bp2 = String::equals;
System. out.println(bp2.test("abc", "abc"));
 
Function<Employee, String> fun = (e) -> e.show();
System. out.println(fun.apply(new Employee()));
Function<Employee, String> fun2 = Employee::show;
System. out.println(fun2.apply(new Employee()));
 
注 : 当需要引用方法的第一个参数是调用对象,并且第二个参数是需要引用方法的第二个参数 (或无参数) 时 :  ClassName::methodName
 
构造器引用
构造器的参数列表,需要与函数式接口中参数列表保持一致 (就是函数签名一致)
1> 类名 :: new
Supplier<Employee> sup = () -> new Employee();
System. out.println(sup.get());
Supplier<Employee> sup2 = Employee::new;
System. out.println(sup2.get());
 
Function<String, Employee> fun = Employee::new;
BiFunction<String, Integer, Employee> fun2 = Employee::new;
 
数组引用
1> 类型[] :: new
Function<Integer, String[]> fun = (args) -> new String[args];
String[] strs = fun.apply(10);
System. out.println(strs.length);
 
Function<Integer, Employee[]> fun2 = Employee[]::new;
Employee[] emps = fun2.apply(20);
System. out.println(emps.length);
构造器引用
构造器的参数列表,需要与函数式接口中参数列表保持一致 (就是函数签名一致)
1> 类名 :: new
Supplier<Employee> sup = () -> new Employee();
System. out.println(sup.get());
Supplier<Employee> sup2 = Employee::new;
System. out.println(sup2.get());
 
Function<String, Employee> fun = Employee::new;
BiFunction<String, Integer, Employee> fun2 = Employee::new;
 
数组引用
1> 类型[] :: new
Function<Integer, String[]> fun = (args) -> new String[args];
String[] strs = fun.apply(10);
System. out.println(strs.length);
 
Function<Integer, Employee[]> fun2 = Employee[]::new;
Employee[] emps = fun2.apply(20);
System. out.println(emps.length);
package cn.jdk.MethodReference;

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;

import javax.annotation.processing.SupportedOptions;

import cn.jdk.lambda.Apple;

/**
 * 
 * JDK8新特性 方法引用
 * 
 * @author 梦见猫、 2018年4月17日
 */
public class MethodReferenceTest {

    public static void main(String[] args) {

        // 1. 第一种方式, 像创建类型一样创建consumer;
        Consumer<String> con = s -> System.out.println(s);
        useConsumer(con, "hello world");

        // 2. 第二种方式, 直接将右边的lambda表达式视为一个对象;
        useConsumer(s -> System.out.println(s), "hello world");

        // 3. 第三种方式, 对象引用System.out里面的方法给lambda表达式调用
        // 为什么可以这样呢? 因为sysout里面的println方法符合consumer的格式, 可以使用consumer格式的表达式进行方法引用
        useConsumer(System.out::println, "Hello world");

        // 下面来看看其他几种类型的表达式如何进行引用
        // 1.1 function 普通的function只能引用静态方法
        // Function<Integer, String> funcMethodReference = String::charAt; -- Cannot
        // make a static reference to the non-static method charAt(int) from the type
        // String;
        Function<String, Integer> funcMethodReference = Integer::parseInt;
        System.out.println(funcMethodReference.apply("12345"));

        // 1.2 BiFunction 加强版function; --> 对象没有实例的时候成员方法的引用
        // T=对象本身的类型 U=参数 R=返回类型
        BiFunction<String, String, Integer> biFunctionMethodReference = String::indexOf;
        // 但是呢, 这种类型的不能这样创建--The method parseInt(String) from the type Integer should be
        // accessed in a static way
        // BiFunction<Integer, String, Integer> biFunctionMethodReference2 =
        // Integer::parseInt;
        System.out.println(biFunctionMethodReference.apply("hello world", "h"));

        // 1.3 对象有实例的时候成员方法的引用;
        // T=参数类型, R=返回值类型;
        String str = "hello world";
        Function<String, Integer> funcMethodReference2 = str::indexOf;
        System.out.println(funcMethodReference2.apply("e"));

        // Supply方法省略;-->有实例entity的get方法;

        // 2. 初始化方法的引用;
        Supplier<String> supplier = String::new;
        String s = supplier.get();
        System.out.println(s);
        
        // 2.1 构造函数参数为1;
        Function<String, Apple> appleFuntion = Apple::new;
        Apple apple = appleFuntion.apply("red");
        System.out.println(apple);

        // 2.2 构造函数参数为2;
        BiFunction<String, Long, Apple> appleFuntion1 = Apple::new;
        Apple apple2 = appleFuntion1.apply("red", 100L);
        System.out.println(apple2);

        // 2.3 如果构造函数大于2怎么办呢? 使用自定义的Function类型进行构造;
        ThreeFunction<String, Long, String, ComplexApple> threeFunction = ComplexApple::new;
        ComplexApple complexApple = threeFunction.apply("Green", 123L, "FuShi");
        System.out.println(complexApple);
        
        //--------------------------------------------------
        // 为什么说表达式可以简化代码呢? 下面我们来看一个例子
        // List的Compare
        List<Apple> list1 = Arrays.asList(new Apple("abc", 123), new Apple("Green", 110), new Apple("red", 124));
        // 1. 最原始的方法;
        Collections.sort(list1, new Comparator<Apple>() {
            @Override
            public int compare(Apple o1, Apple o2) {
                return (int) (o1.getWeight() - o2.getWeight());
            }
        });
        System.out.println(list1);
        
        // 2. lambda表达式
        List<Apple> list2 = Arrays.asList(new Apple("abc", 123), new Apple("Green", 110), new Apple("red", 124));
        list2.sort((o1, o2)-> (int)o1.getWeight() - (int)o2.getWeight());
        System.out.println(list2);
        
        // 3. 最强简写;
        List<Apple> list3 = Arrays.asList(new Apple("a", 123), new Apple("b", 110), new Apple("red", 124));
        // 可以理解为Apple::getColor就是一个function, 方法引用的几种类型都是返回function
        list3.sort(Comparator.comparing(Apple::getColor));
        System.out.println(list3);
        
        Function<Long, Long> funcLong = x -> x+1;
    }

    /**
     * 这个方法传入一个consumer类型的lambda表达式, 可以有三种方法进行优化
     * @param consumer
     * @param t
     */
    private static <T> void useConsumer(Consumer<T> consumer, T t) {
        consumer.accept(t);
    }

}

最强简写可能比较难理解, 这里我们贴源码解释一下:

public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
            Function<? super T, ? extends U> keyExtractor)
    {
        Objects.requireNonNull(keyExtractor);
        return (Comparator<T> & Serializable)
            (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
    }
Apple::getColor刚好作为方法引用成为一个function为c1和c2提供了值.

猜你喜欢

转载自www.cnblogs.com/unclecc/p/9396763.html