面向物件2
阿新 • • 發佈:2020-08-29
Lambda表示式
匿名內部類仍然有很多冗餘,為了更簡潔,JDK8加入了Lambda表示式:
(引數型別 引數名稱) -> { 程式碼 }
注意:
- Lambda必須具有介面,且介面中只有一個抽象方法
- 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()); } } }
省略格式:
- 小括號內參數可以省略
- 如果只有一個引數,括號也可以省略
- 如果程式碼只有一行,花括號/return/分號都可以省略
函式式介面
函式式介面:有且僅有一個抽象方法的介面,也就是Lambda使用的介面。
特點:
- 函式前面加一個
@FunctionalInterface
叫註解,用於檢查介面是否是一個函式式介面。 - 函式式介面一般作為方法的引數或返回值
- 匿名內部類會建立一個.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
包中提供了大量的函式式介面:
Supplier
Consumer
:不生產資料,而是消費資料Predicate
:對資料判斷,返回一個布林值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));
}
}
獲取流:
- JDK8之後,在Collection中加入了default方法stream(),其子類可以直接呼叫。
- Stream介面的靜態方法of可以獲取陣列對應的流。
流中的方法:
- 延遲方法:返回值仍是Stream流,可以鏈式程式設計
- 終結方法:
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);
}