jdk8新特性之雙冒號 :: 用法及詳解


jdk8的新特性有很多,最亮眼的當屬函數式編程的語法糖,本文主要講解下雙冒號::的用法。

概念

類名::方法名,相當於對這個方法閉包的引用,類似js中的一個function。比如:

Function<String,String> func =  String::toUpperCase;

 

(Function在java.util.function包下,也是jdk8新加入的類,同級目錄下有很多函數式編程模型接口,比如Consumer/Predicate/Operator等)

 

func相當於一個入參和出參都為String的函數,可以直接

func.apply("abc")

 

接收一個參數,返回一個結果("ABC")。也可以用於代替下面的Lambda表達式:

List<String> l = Arrays.asList("a","b","c");
l.stream().map(s -> s.toUpperCase());
l.stream().map(func);

 

下面自定義一個函數式接口

public class MyConsumer<String> implements Consumer<String> {
    @Override
    public void accept(String s) {
        System.out.println(s);
    }
}

 

下面這倆種寫法等價:

List<String> l = Arrays.asList("a","b","c");

l.forEach(new MyConsumer<>());
l.forEach(s -> System.out.println(s));

 

但是,這種寫法卻不行,編譯失敗:

l.forEach(MyConsumer::accept);

 

因為MyConsumer的accept方法不是靜態的,如果想使用這個方法,需要一個實例,還需要一個入參,共倆個參數。而List.forEach中需要的是consumer類型,相當於s -> {...},只有一個參數。

 

下面詳細分析雙冒號使用的各種情況

新建一個類,里面聲明四個代表各種情況的方法:

public class DoubleColon {

    public static void printStr(String str) {
        System.out.println("printStr : " + str);
    }

    public void toUpper(){
        System.out.println("toUpper : " + this.toString());
    }

    public void toLower(String str){
        System.out.println("toLower : " + str);
    }

    public int toInt(String str){
        System.out.println("toInt : " + str);
        return 1;
    }
}

 

把它們用::提取為函數,再使用:

Consumer<String> printStrConsumer = DoubleColon::printStr;
printStrConsumer.accept("printStrConsumer");

Consumer<DoubleColon> toUpperConsumer = DoubleColon::toUpper;
toUpperConsumer.accept(new DoubleColon());

BiConsumer<DoubleColon,String> toLowerConsumer = DoubleColon::toLower;
toLowerConsumer.accept(new DoubleColon(),"toLowerConsumer");

BiFunction<DoubleColon,String,Integer> toIntFunction = DoubleColon::toInt;
int i = toIntFunction.apply(new DoubleColon(),"toInt");

 

非靜態方法的第一個參數為被調用的對象,后面是入參。靜態方法因為jvm已有對象,直接接收入參。

 

再寫一個方法使用提取出來的函數:

public class TestBiConsumer {
    public void test(BiConsumer<DoubleColon,String> consumer){
        System.out.println("do something ...");
    }
}

 

下面這倆種傳入的函數是一樣的:

TestBiConsumer obj = new TestBiConsumer();
obj.test((x,y) -> System.out.println("do something ..."));
obj.test(DoubleColon::toLower);

 

總結

用::提取的函數,最主要的區別在於靜態與非靜態方法,非靜態方法比靜態方法多一個參數,就是被調用的實例。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM