如何提高java代碼優雅度,java8中函數式編程是不得不提的一個重要模塊。下面直奔主題:
一.函數式接口的定義
-
被@FunctionalInterface註釋的接口,滿足@FunctionalInterface註釋的約束;
- 沒有被@FunctionalInterface註釋的接口,但是滿足@FunctionalInterface註釋的約束。
二.@FunctionalInterface註釋的約束
- 接口有且只能有個一個抽象方法,只有方法定義,沒有方法體;
- 在接口中覆寫Object類中的public方法,不算是函數式接口的方法。
示例:
/**
*
* @author zhaoj
* @version FunctionInterfaceTest.java, v 0.1 2019-09-04 17:53
*/
@FunctionalInterface
public interface FunctionInterfaceTest {
/**
* 自定義方法
* @param input
* @return
*/
String fetch(String input);
/**
* Object中的方法
*/
@Override
String toString();
/**
* Object中的方法
* @param obj
* @return
*/
@Override
boolean equals(Object obj);
}
三.函數式接口實例的創建
函數式接口實例的創建有三種方式:
1、lambda表達式;
2、方法引用;
3、構造方法引用。
三種示例:
/**
*
* @author zhaoj
* @version FunctionalInterfaceTest0.java, v 0.1 2019-09-04 17:51
*/
public class FunctionalInterfaceTest0 {
public static void main(String[] args) {
/**
* 1、lambda表達式
*/
FunctionInterfaceTest functionInterfaceTest1 = item -> item;
/**
* 2、方法引用
*/
FunctionInterfaceTest functionInterfaceTest2 = FunctionalInterfaceTest0::getInstance;
FunctionInterfaceTest functionInterfaceTest3 = FunctionalInterfaceTest0::getMessage;
String msg1 = join("hello",functionInterfaceTest2);
System.out.println(msg1);
String msg2 = join("hello",functionInterfaceTest3);
System.out.println(msg2);
/**
* 3、構造方法引用
*/
FunctionInterfaceTest functionInterfaceTest4 = String::new;
}
public static String getInstance(String item){
return item+"!world";
}
public static String getMessage(String massage){
return "world,"+ massage+"!";
}
public static String join(String str,FunctionInterfaceTest functionTest){
return functionTest.fetch(str);
}
四.java8中常用的函數式接口
常用的函數式接口主要有四種類型,是通過其輸入和輸出的參數來進行區分的:
接口名 |
說明 |
Function<T,R> |
接收一個T類型的參數,返回一個R類型的結果 |
Consumer<T> |
接收一個T類型的參數,不返回值 |
Predicate<T> |
接收一個T類型的參數,返回一個boolean類型的結果 |
Supplier<T> |
不接受參數,返回一個T類型的結果 |
示例:
/**
* @author zhaoj
* @version FunctionalInterfaceTest1.java, v 0.1 2019-09-05 9:34
*/
public class FunctionalInterfaceTest1 {
public static void main(String[] args) {
//1.第一種創建方式 接收一個T類型的參數,返回一個R類型的結果
Function<String, String> function1 = item -> item + "返回值";
//第二種創建方式,接收一個T類型的參數,不返回值,lambda語句,使用大括號,沒有return關鍵字,表示沒有返回值
Consumer<String> function2 = item -> {
System.out.println(item);
};
//第三種創建方式,接收一個T類型的參數,返回一個boolean類型的結果
PredicateTest<String> function3 = item -> "".equals(item);
//第四種創建方式,不接受參數,返回一個T類型的結果
Supplier<String> function4 = () -> new String("123");
/**
* 1、創建一個String類型的集合
* 2、將集合中的所有元素的末尾追加字符串'1'
* 3、選出長度大於2的字符串
* 4、遍歷輸出所有元素
*/
List<String> list = Arrays.asList("zhangsan", "lisi", "wangwu", "xiaoming", "zhaoliu");
list.stream()
//傳入的是一個Function函數式接口
.map(value -> value + "666")
//傳入的是一個Predicate函數式接口
.filter(value -> value.length() > 5)
//傳入的是一個Consumer函數式接口
.forEach(value -> {
System.out.println(value);
});
}
}
Java8中對於接收兩個參數的場景提供了相關的函數式接口:
接口名 |
說明 |
BiFunction<T, U, R> |
接收T類型和U類型的兩個參數,返回一個R類型的結果 |
BiConsumer<T , U> |
接收T類型和U類型的兩個參數,不返回值 |
BiPredicate<T, U> |
接收T類型和U類型的兩個參數,返回一個boolean類型的結果 |
示例:
/**
*
* @author zhaoj
* @version FunctionalInterfaceTest2.java, v 0.1 2019-09-05 17:43
*/
public class FunctionalInterfaceTest2 {
public static void main(String[] args) {
/**
* Bi類型的接口創建
*/
BiFunction<String, String, Integer> biFunction = (str1, str2) -> str1.length() + str2.length();
BiConsumer<String, String> biConsumer = (str1, str2) -> System.out.println(str1 + str2);
BiPredicate<String, String> biPredicate = (str1, str2) -> str1.length() > str2.length();
}
/**
* Bi類型的接口使用
*/
//輸出10
int length = getLength("hello", "world", (str1, str2) -> str1.length() + str2.length());
//輸出false
boolean boolean1 = getBoolean("hello", "world", (str1, str2) -> str1.length() > str2.length());
System.out.println(length);
System.out.println(boolean1);
//沒有輸出
noResult("hello", "world", (str1, str2) -> System.out.println(str1 + " " + str2));
}
public static int getLength(String str1, String str2, BiFunction<String, String, Integer> function) {
return function.apply(str1, str2);
}
public static void noResult(String str1, String str2, BiConsumer<String, String> biConcumer) {
biConcumer.accept(str1, str2);
}
public static boolean getBoolean(String str1, String str2, BiPredicate<String, String> biPredicate) {
return biPredicate.test(str1, str2);
}
}
關於多個參數值的使用,無論實在Function接口中,還是在BI類型的接口都提供了類似的操作。(注:java8中,接口的方法是可以有實現的,但需要default關鍵字修飾,這是其他版本的jdk沒有的特性)
1、 Function接口的andThen方法和compose方法
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
解釋說明:
Compose方法:方法接收一個Function類型的參數,返回一個值。這也是一個標準的Function類型的定義。在compose方法內部也有一個apply方法。在執行compose方法中的apply方法之前,它先執行了before接口的apply方法,也是compose方法的輸入參數。然後將before方法執行的返回值作爲compose中apply方法的輸入參數。
andThen方法:該方法與compose方法很類似。不同之處在於,andThen是先執行自身的apply方法,將apply的返回值作爲after接口的輸入值。相對於compose方法,只是順序的不同。
2、Consumer接口的andThen方法
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
解釋說明:
將輸入參數分別賦給andThen內部的accept方法和after內部的accept方法。After的計算在andThen之後,起到了後置連接的作用。在這裏沒有compose方法,因爲後置連接反過來就是前置連接,所以不需要一個多餘的compose方法了。只需要在傳遞時,交換兩個consumer的順序即可。
3、 predicate接口的and、or、negate方法
default Predicate<T> and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
default Predicate<T> negate() {
return (t) -> !test(t);
}
default Predicate<T> or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
解釋說明:
分別是與 ,非,或操作。
此外,java8針對原生類型int,long,double都提供了相應的函數式接口,使用方式都相同,見java.util.function包。
五.自定義使用
//自定義T,U兩個入參,無返回參數,用於構建組裝信息
@FunctionalInterface
public interface Builder<T, U> {
void build(T t, U u);
}
//自定義T,U兩個入參,返回驗證結果
@FunctionalInterface
public interface Verification<T,U> {
boolean verify(T t, U u);
}
//自定義工具類
public class Util {
public static <T, U> List<T> buildParam(List<T> left, List<U> right, Verification<T, U> verifyer, Builder<T, U> builder) {
if (left.isEmpty() || right.isEmpty()) {
return left;
}
left.forEach(l -> right.forEach(r -> {
if (verifyer.verify(l, r)) {
builder.build(l, r);
return;
}
}));
return left;
}
}