1. 程式人生 > 實用技巧 >面向物件2

面向物件2

Lambda表示式

匿名內部類仍然有很多冗餘,為了更簡潔,JDK8加入了Lambda表示式:

(引數型別 引數名稱) -> { 程式碼 }

注意:

  1. Lambda必須具有介面,且介面中只有一個抽象方法
  2. Lambda必須有上下文才能推斷,也就是區域性變數等

比如:有個Person類,裡面有name和age,做排序:

import java.util.Arrays;

public class Test {
    public static void main(String[] args) {
        Person[] arr = {
                new Person("張三", 19),
                new Person("李四", 18)};
        Arrays.sort(arr, (Person a, Person b) -> {
            return a.getAge() - b.getAge();
        });
        for (Person p : arr) {
            System.out.println(p.getName() + " " + p.getAge());
        }
    }
}

省略格式:

  1. 小括號內參數可以省略
  2. 如果只有一個引數,括號也可以省略
  3. 如果程式碼只有一行,花括號/return/分號都可以省略

函式式介面

函式式介面:有且僅有一個抽象方法的介面,也就是Lambda使用的介面。

特點:

  1. 函式前面加一個@FunctionalInterface叫註解,用於檢查介面是否是一個函式式介面。
  2. 函式式介面一般作為方法的引數或返回值
  3. 匿名內部類會建立一個.class檔案,載入到記憶體;Lambda表示式不會;

如,函式式介面

@FunctionalInterface
interface MyFunctionInterface {
    public abstract void method();
}

函數語言程式設計:函式式介面作為方法的引數

public class Test {
    public static void main(String[] args) {
        startThread(() -> System.out.println("開啟執行緒任務"));
    }

    public static void startThread(Runnable run) {
        new Thread(run).start();
    }
}

java.util.function包中提供了大量的函式式介面:

  1. Supplier
    :傳入一個型別的資料 ,返回該型別資料
  2. Consumer:不生產資料,而是消費資料
  3. Predicate:對資料判斷,返回一個布林值
  4. Function:輸入一個數據型別,轉換成另一個數據型別

Stream流

Stream流和IO流不一樣,JDK1.8之後,提出Stream流,是為了解決已有集合類庫的弊端。

public class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("張三");
        list.add("李四");
        list.add("張陽");
        list.add("王老五");
        // 兩個過濾,一個遍歷
        list.stream()
                .filter(name->name.startsWith("張"))
                .filter(name->name.length()==2)
                .forEach(name-> System.out.println(name));
    }
}

獲取流:

  1. JDK8之後,在Collection中加入了default方法stream(),其子類可以直接呼叫。
  2. Stream介面的靜態方法of可以獲取陣列對應的流。

流中的方法:

  1. 延遲方法:返回值仍是Stream流,可以鏈式程式設計
  2. 終結方法:count()forEach() 。stream流在使用完終結方法,將不能再使用。

forEach遍歷【終結方法】

Stream<String> stream = Stream.of("張三", "李四", "王五", "趙六");
// forEach方法接收一個Consumer介面函式, Consumer.accept()消費一個數據
stream.forEach((String name) -> System.out.println(name));

filter過濾

Stream<String> stream = Stream.of("張三", "李四", "張陽", "趙六");
// filter方法接收一個Predicate介面函式, Predicate.test()返回布林值
Stream<String> stream2 =  stream.filter((String name) -> name.startsWith("張"));
stream2.forEach((String name) -> System.out.println(name));

map型別轉換

Stream<String> stream = Stream.of("1", "2", "3", "4");
// map方法接收一個Function介面函式, Function.apply()轉換資料型別
Stream<Integer> stream2 =  stream.map((String x) -> Integer.parseInt(x));
stream2.forEach((Integer x) -> System.out.println(x));

count統計個數【終結方法】

Stream<String> stream = Stream.of("1", "2", "2", "4");
System.out.println(stream.count());

limit擷取前幾個,skip跳過前幾個

Stream<String> stream = Stream.of("張三", "李四", "王五", "趙六");
Stream<String> stream2  = stream.limit(3).skip(1);
stream2.forEach((name) -> System.out.println(name));

concat合併兩個流

Stream<String> stream = Stream.of("張三");
Stream<String> stream2 = Stream.of("老王");
Stream<String> stream3  = Stream.concat(stream, stream2);
stream3.forEach((name) -> System.out.println(name));

方法引用

Lambda表示式:拿什麼引數做什麼操作。如果操作方案已經存在,那就沒有必要再寫一遍 。

方法引用:用雙冒號::表示引用運算子,它所在的表示式表示方法引用。如果Lambda要表達的函式方案存在於某個方法的實現中,那麼可以通過雙冒號來引用該方法作為Lambda的替代者。

例如,System.out物件中有一個過載的println(s)方法是我們需要的,那麼下面兩種寫法等效:

  • Lambda表示式:(String s) -> System.out.println(s) ,表示引數通過Lambda傳遞給println
  • 方法引用:Systeml.out::println ,表示引數直接用println列印

函式式介面是Lambda的基礎,方法引用是Lambda的孿生兄弟。

通過物件名引用成員方法:

// Lambda表示式
printString((s) -> {
	MethodRerObject obj = new MethodRerObject();
	obj.printUpperCaseString(s);
});
        
// 方法引用:有物件,有物件中的方法,那麼直接引用該方法
MethodRerObject obj2 = new MethodRerObject();
printString(obj2::printUpperCaseString);

通過類名引用靜態成員方法:

// Math類中有一個abs()靜態方法
method(Math::abs);
// 自己傳遞數
method2(-10, Math::abs);

通過super引用成員方法:

// 在子類內有一個方法,是呼叫父類的方法傳遞給函式式介面
// Lambda表示式
method(()->new FuClass().sayHello());
method(()->super.sayHello());
// 引用方法
method(super::sayHello);

通過this引用成員方法:

// 和用super差不多
// Lambda表示式
method(()->this.sayHello());
// 引用方法
method(this::sayHello);

類的構造器引用:

// 有一個類,以及類的函式式介面
printName("張三", (name) ‐> new Person(name));
// 方法引用
printName("張三", Person::new);

陣列的構造器引用:

陣列也是Object子類物件,也有構造器,用法和類的構造器一樣。

public class Test {
    public static void main(String[] args) {
        // Lambda表示式
        int[] array = initArray(10, (length) -> new int[length]);
        // 方法引用
        int[] array2 = initArray(10, int[]::new);
    }

    public static int[] initArray(int length, ArrayBuilder b) {
        return b.buildArray(length);
    }
}

// 函式式介面
@FunctionalInterface
interface ArrayBuilder {
    int[] buildArray(int length);
}