天天看點

Java8-1-新特性_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 表達式的類型。

Java8-1-新特性_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();};

四.方法引用

如何建構方法引用

方法引用主要有三類。

(1) 指向靜态方法的方法引用(例如 Integer 的 parseInt 方法,寫作 Integer::parseInt )。

(2) 指 向 任 意 類 型 實 例 方 法 的 方 法 引 用 ( 例 如 String 的 length 方 法 , 寫 作 String::length )

(3) 指向現有對象的執行個體方法的方法引用(假設你有一個局部變量 expensiveTransaction

用于存放 Transaction 類型的對象,它支援執行個體方法 getValue ,那麼你就可以寫 expensive-Transaction::getValue )

Java8-1-新特性_Lambda表達式
Java8-1-新特性_Lambda表達式

通俗來講就是 方法引用首先是建立在熟悉前面幾種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);

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提供了值.      

   如果實在不能了解呢, 可以看一下https://www.jianshu.com/p/93de07fc6f03裡面的方法引用實戰.

轉載于:https://www.cnblogs.com/unclecc/p/9396763.html