函式式編程
面向物件過分強調“必須通過物件的形式來做事情”,而函式式思想則盡量忽略面向物件的復雜語法——強調做什么,而不是怎么做,
有時只是為了做某事情而不得不創建一個物件,而傳遞一段代碼才是我們真正的目的,
Lambda
Lambda是一個匿名函式,可以理解為一段可以傳遞的代碼,
當需要啟動一個執行緒去完成任務時, 通常會通過java.lang.Runnable介面來定義任務內容,并使用java.lang.Thread類來啟動該執行緒
傳統寫法,代碼如下:
public class Demo {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("多執行緒任務執行!");
}
}).start();
}
}
借助Java 8的全新語法,上述Runnable介面的匿名內部類寫法可以通過更簡單的Lambda運算式達到同樣的效果:
public class Demo04LambdaRunnable {
public static void main(String[] args) {
new Thread(() -> System.out.println("多執行緒任務執行!")).start(); // 啟動執行緒
}
}
Lambda的優點 簡化匿名內部類的使用,語法更加簡單,
前提條件
必須是介面, 介面中有且只有一個抽象方法
有且僅有一個抽象方法的介面,稱為函式式介面,
格式
Lambda運算式的標準格式為:
() -> {}
() 引數串列,無引數留空
-> 固定寫法, 代表指向動作
{} 方法體
省略規則
在Lambda標準格式的基礎上,使用省略寫法的規則為:
- 引數型別可省略
- 如果只有一個引數 ()可以省略
- 如果方法體只有一句話 return 大括號 分號都可省略, 但必須同時省略
new Thread(() -> System.out.println("省略格式開啟執行緒")).start();
原理
- 在匿名方法所在類中新增一個方法,方法體為Lambda運算式中的代碼
- 運行時形成一個新的類,并實作對應介面
- 重寫方法的方法體中呼叫匿名方法所在類中新生成的方法.
函式式介面
函式式介面在Java中是指:有且僅有一個抽象方法的介面,
函式式介面,即適用于函式式編程場景的介面,而Java中的函式式編程體現就是Lambda,所以函式式介面就是可以適用于Lambda使用的介面,
從應用層面來講,Java中的Lambda可以看做是匿名內部類的簡化格式,但是二者在原理上不同,
格式
只要確保介面中有且僅有一個抽象方法即可:
修飾符 interface 介面名稱 {
public abstract 回傳值型別 方法名稱(可選引數資訊);
}
由于介面當中抽象方法的public abstract是可以省略的,所以定義一個函式式介面很簡單:
public interface MyFunctionalInterface {
void myMethod();
}
@FunctionalInterface
@FunctionalInterface 該注解可用于一個介面的定義上:
@FunctionalInterface
public interface MyFunctionalInterface {
void myMethod();
}
一旦使用該注解來定義介面,編譯器將會強制檢查該介面是否確實有且僅有一個抽象方法,否則將會報錯,不過,即使不使用該注解,只要滿足函式式介面的定義,這仍然是一個函式式介面
常用函式式介面
Supplier介面
java.util.function.Supplier<T>介面,它意味著"供給" , 對應的Lambda運算式需要“對外提供”一個符合泛型型別的物件資料,
抽象方法:
T get() 用來獲取一個泛型引數指定型別的物件資料
求陣列元素最大值
使用Supplier介面作為方法引數型別,通過Lambda運算式求出int陣列中的最大值
public class supplierInterface {
public static void main(String[] args) {
int[] arr = {3,24,346,4,13};
method(() -> {
Arrays.sort(arr);
return arr[arr.length - 1];
});
}
public static void method(Supplier<Integer> s) {
Integer max = s.get();
System.out.println(max);
}
}
Consumer介面
java.util.function.Consumer<T> 介面不生產資料,而是消費一個資料,其資料型別由泛型引數決定
抽象方法
void accept(T t),意為消費一個指定泛型的資料
默認方法
default Consumer<T> andThen(Consumer<? super T> after)
public class _4_consumerInterface {
public static void main(String[] args) {
method("Hello World", (String s) -> {
System.out.println(s.toUpperCase());
});
method("HEllO WorlD", s -> System.out.println(s.toLowerCase()));
System.out.println("==========================");
method("HEllO WorlD", (String s) -> {
System.out.println(s.toUpperCase());
}, (String s) -> {
System.out.println(s.toLowerCase());
});
method("HEllO WorlD",
s -> System.out.println(s.toUpperCase()),
s -> System.out.println(s.toLowerCase())
);
}
public static void method(String s, Consumer<String> c) {
c.accept(s);
}
public static void method(String s, Consumer<String> c1, Consumer<String> c2) {
// c1.accept(s);
// c2.accept(s);
// andThen c1.accept(s)后執行c2.accept(s) 等同于上面的寫法
c1.andThen(c2).accept(s);
}
}
Function介面
java.util.function.Function<T,R> 介面用來根據一個型別的資料得到另一個型別的資料,前者稱為前置條件,后者稱為后置條件
抽象方法:
R apply(T t) 根據型別T的引數獲取型別R的結果
public class Test {
public static void main(String[] args) {
Function<String,Integer> f = new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s);
}
};
Integer apply = f.apply("100");
System.out.println(apply);
Function<String,Integer> f2 = s -> Integer.parseInt(s);
System.out.println(f2.apply("200"));
}
}
默認方法:andThen
Function介面中有一個默認的andThen方法,用來進行組合操作,與Consumer介面相同
public class Test {
public static void main(String[] args) {
method5("10", (String s) -> {
return Integer.parseInt(s);
}, (Integer i) -> {
return i * 10;
});
method5("100", s -> Integer.parseInt(s), i -> i * 10);
method5("1000", Integer::parseInt, i -> i * 10);
}
private static void method5(String s, Function<String, Integer> f1, Function<Integer, Integer> f2) {
// Integer i = f1.apply(s);
// Integer n = f2.apply(i);
Integer n = f1.andThen(f2).apply(s);
System.out.println(n);
}
}
Function的前置條件泛型和后置條件泛型可以相同
Predicate介面
java.util.function.Predicate
抽象方法: boolean test(T t) 回傳boolean
public class predicateInterface {
public static void main(String[] args) {
method("HelloWorld.java", (String s) -> {
return s.toLowerCase().endsWith(".java");
});
method("Hello.java", s -> s.toLowerCase().endsWith(".java"));
}
public static void method(String filename, Predicate<String> p) {
boolean b = p.test(filename);
System.out.println(b);
}
}
默認方法
Predicate<T> and(Predicate<? super T> other) 并且, 底層使用 &&
Predicate<T> or(Predicate<? super T> other) 或者, 底層使用 ||
Predicate<T> negate() 取反, 底層使用 !
public class Test {
public static void main(String[] args) {
method("Helloworld" ,s -> s.contains("H"), s -> s.contains("W"));
}
private static void method(String str ,Predicate<String> one, Predicate<String> two) {
boolean b1 = one.test(str);
boolean b2 = two.test(str);
System.out.println("字串符合要求嗎:" + (b1 && b2));
boolean isValid = one.and(two).test(str);
System.out.println("字串符合要求嗎:" + isValid);
}
}
public class Test {
public static void main(String[] args) {
method("Helloworld" ,s -> s.contains("H"), s -> s.contains("W"));
}
private static void method(String str ,Predicate<String> one, Predicate<String> two) {
boolean b1 = one.test(str);
boolean b2 = two.test(str);
System.out.println("字串符合要求嗎:" + (b1 || b2));
boolean isValid = one.or(two).test(str);
System.out.println("字串符合要求嗎:" + isValid);
}
}
public class Test {
public static void main(String[] args) {
isLong("aaa", new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length()<5;
}
});
isLong("bbbaa",s -> s.length()>=5);
}
public static void isLong(String s , Predicate<String> p){
boolean test = p.test(s);
System.out.println(!test);
boolean b2 = p.negate().test(s);
System.out.println(b2);
}
}
方法參考
前提
Lambda運算式中只有一句話時 可能使用
格式
符號表示 : ::
符號說明 : 雙冒號為方法參考運算子,而它所在的運算式被稱為方法參考,
推導與省略 : ** 如果使用Lambda,那么根據“可推導就是可省略**”的原則,無需指定引數型別,也無需指定的多載形式——它們都將被自動推導
應用Lambda運算式 , 在accept方法中接收字串 , 目的就是為了列印顯示字串 , 那么通過Lambda來使用它的代碼很簡單:
public class DemoPrintSimple {
private static void printString(Consumer<String> data, String str) {
data.accept(str);
}
public static void main(String[] args) {
printString(s -> System.out.println(s), "Hello World");
}
}
使用方法參考進行簡化
public class DemoPrintRef {
private static void printString(Consumer<String> data, String str) {
data.accept(str);
}
public static void main(String[] args) {
printString(System.out::println, "HelloWorld");
}
}
其他參考
public class _5_functionInterface {
public static void main(String[] args) {
method("100", (String s) -> {
return Integer.parseInt(s);
});
method("10", s -> Integer.parseInt(s));
/*
類名參考靜態方法
類名::方法名
*/
method("1000", Integer::parseInt);
/*
類名參考構造方法
類名::new
*/
method2("張三", Person::new);
method2("李四", Person::new);
method2("王五", s -> new Person(s));
method3(Person::new);
/*
陣列參考構造方法
資料型別[]::new
*/
method4(5,int[]::new);
method4(3, (Integer i) -> {
return new int[i];
});
method4(1, i -> new int[i]);
}
public static void method(String s, Function<String, Integer> f) {
Integer n = f.apply(s);
System.out.println(n);
}
private static void method2(String s, Function<String, Person> f) {
Person p = f.apply(s);
System.out.println(p);
}
private static void method3(Supplier<Person> su) {
Person p = su.get();
System.out.println(p);
}
private static void method4(Integer i, Function<Integer, int[]> f) {
int[] arr = f.apply(i);
System.out.println(Arrays.toString(arr));
}
}
class Person {
private String name;
public Person() {}
public Person(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/550639.html
標籤:其他
上一篇:UML類圖
下一篇:返回列表
