版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進行舉報或認領(lǐng)
文檔簡介
\hJava常見問題的簡單解法目錄\h第1章基礎(chǔ)知識\h1.1lambda表達式\h問題\h方案\h討論\h1.2方法引用\h問題\h方案\h討論\h另見\h1.3構(gòu)造函數(shù)引用\h問題\h方案\h討論\h另見\h1.4函數(shù)式接口\h問題\h方案\h討論\h另見\h1.5接口中的默認方法\h問題\h方案\h討論\h另見\h1.6接口中的靜態(tài)方法\h問題\h方案\h討論\h另見\h第2章java.util.function包\h2.1Consumer接口\h問題\h方案\h討論\h另見\h2.2Supplier接口\h問題\h方案\h討論\h另見\h2.3Predicate接口\h問題\h方案\h討論\h另見\h2.4Function接口\h問題\h方案\h討論\h另見\h第3章流式操作\h3.1流的創(chuàng)建\h問題\h方案\h討論\h另見\h3.2裝箱流\h問題\h方案\h討論\h另見\h3.3利用reduce方法實現(xiàn)歸約操作\h問題\h方案\h討論\h另見\h3.4利用reduce方法校驗排序\h問題\h方案\h討論\h另見\h3.5利用peek方法對流進行調(diào)試\h問題\h方案\h討論\h3.6字符串與流之間的轉(zhuǎn)換\h問題\h方案\h討論\h另見\h3.7獲取元素數(shù)量\h問題\h方案\h討論\h另見\h3.8匯總統(tǒng)計\h問題\h方案\h討論\h另見\h3.9查找流的第一個元素\h問題\h方案\h討論\h另見\h3.10使用anyMatch、allMatch與noneMatch方法\h問題\h方案\h討論\h另見\h3.11使用flatMap與map方法\h問題\h方案\h討論\h另見\h3.12流的拼接\h問題\h方案\h討論\h另見\h3.13惰性流\h問題\h方案\h討論\h另見\h第4章比較器與收集器\h4.1利用比較器實現(xiàn)排序\h問題\h方案\h討論\h4.2將流轉(zhuǎn)換為集合\h問題\h方案\h討論\h另見\h4.3將線性集合添加到映射\h問題\h方案\h討論\h另見\h4.4對映射排序\h問題\h方案\h討論\h另見\h4.5分區(qū)與分組\h問題\h方案\h討論\h另見\h4.6下游收集器\h問題\h方案\h討論\h另見\h4.7查找最大值和最小值\h問題\h方案\h討論\h另見\h4.8創(chuàng)建不可變集合\h問題\h方案\h討論\h另見\h4.9實現(xiàn)Collector接口\h問題\h方案\h討論\h另見\h第5章流式操作、lambda表達式與方法引用的相關(guān)問題\h5.1java.util.Objects類\h問題\h方案\h討論\h5.2lambda表達式與效果等同于final的變量\h問題\h方案\h討論\h另見\h5.3隨機數(shù)流\h問題\h方案\h討論\h另見\h5.4Map接口的默認方法\h問題\h方案\h討論\h5.5默認方法沖突\h問題\h方案\h討論\h另見\h5.6集合與映射的迭代\h問題\h方案\h討論\h另見\h5.7利用Supplier創(chuàng)建日志消息\h問題\h方案\h討論\h另見\h5.8閉包復(fù)合\h問題\h方案\h討論\h另見\h5.9利用提取的方法實現(xiàn)異常處理\h問題\h方案\h討論\h另見\h5.10受檢異常與lambda表達式\h問題\h方案\h討論\h另見\h5.11泛型異常包裝器的應(yīng)用\h問題\h方案\h討論\h另見\h第6章Optional類\h6.1Optional的創(chuàng)建\h問題\h方案\h討論\h另見\h6.2從Optional中檢索值\h問題\h方案\h討論\h另見\h6.3getter和setter方法中的Optional\h問題\h方案\h討論\h另見\h6.4Optional.flatMap與Optional.map方法\h問題\h方案\h討論\h另見\h6.5Optional的映射\h問題\h方案\h討論\h另見\h第7章文件I/O\h7.1文件處理\h問題\h方案\h討論\h另見\h7.2以流的形式檢索文件\h問題\h方案\h討論\h另見\h7.3文件系統(tǒng)的遍歷\h問題\h方案\h討論\h另見\h7.4文件系統(tǒng)的搜索\h問題\h方案\h討論\h另見\h第8章java.time包\h8.1Date-TimeAPI中的基本類\h問題\h方案\h討論\h另見\h8.2根據(jù)現(xiàn)有實例創(chuàng)建日期和時間\h問題\h方案\h討論\h另見\h8.3調(diào)節(jié)器與查詢\h問題\h方案\h討論\h8.4將java.util.Date轉(zhuǎn)換為java.time.LocalDate\h問題\h方案\h討論\h8.5解析與格式化\h問題\h方案\h討論\h8.6查找具有非整數(shù)小時偏移量的時區(qū)\h問題\h方案\h討論\h8.7根據(jù)UTC偏移量查找地區(qū)名\h問題\h方案\h討論\h8.8獲取事件之間的時間\h問題\h方案\h討論\h第9章并行與并發(fā)\h9.1將順序流轉(zhuǎn)換為并行流\h問題\h方案\h討論\h9.2并行流的優(yōu)點\h問題\h方案\h討論\h9.3調(diào)整線程池大小\h問題\h方案\h討論\h另見\h9.4Future接口\h問題\h方案\h討論\h另見\h9.5完成CompletableFuture\h問題\h方案\h討論\h另見\h9.6多個CompletableFuture之間的協(xié)調(diào)(第1部分)\h問題\h方案\h討論\h另見\h9.7多個CompletableFuture之間的協(xié)調(diào)(第2部分)\h問題\h方案\h討論\h另見\h第10章Java9新特性\h10.1Jigsaw中的模塊\h問題\h方案\h討論\h另見\h10.2接口中的私有方法\h問題\h方案\h討論\h10.3創(chuàng)建不可變集合\h問題\h方案\h討論\h另見\h10.4新增的Stream方法\h問題\h方案\h討論\h10.5下游收集器:filtering與flatMapping\h問題\h方案\h討論\h另見\h10.6新增的Optional方法\h問題\h方案\h討論\h另見\h10.7日期范圍\h問題\h方案\h討論\h另見\h附錄A泛型與Java8\hA.1背景\hA.2眾所周知的事實\hA.3容易忽略的事實\hA.4通配符與PECS\hA.4.1無界通配符\hA.4.2上界通配符\hA.4.3下界通配符\hA.4.4PECS原則\hA.4.5多重邊界\hA.5Java8API示例\hA.5.1Stream.max方法\hA.5.2Stream.map方法\hA.5.3Cparing方法\hA.5.4Map.EparingByKey與Map.EparingByValue方法\hA.5.5類型擦除\hA.6小結(jié)注:原文檔電子版(非掃描),需要的請下載本文檔后留言謝謝。第1章基礎(chǔ)知識Java8的最大變化是引入了函數(shù)式編程(functionalprogramming)的概念。具體而言,Java8增加了lambda表達式(lambdaexpression)、方法引用(methodreference)和流(stream)。如果讀者尚未接觸過這些新增的函數(shù)式特性,或許會驚訝于寫出的代碼和之前大相徑庭。Java8的變化堪稱迄今為止這門語言最大的變化。從許多方面來說,這與學(xué)習(xí)一門全新的語言并無二致。讀者或許會問,這樣做的目的是什么?為什么要對一門已有20年歷史且計劃保持向后兼容性的語言做出如此巨大的改變?一門為各方所公認的成熟語言,是否需要這些重大的修改?作為這些年來最成功的面向?qū)ο笳Z言之一,Java為何要轉(zhuǎn)向函數(shù)式范式?答案在于,軟件開發(fā)領(lǐng)域已經(jīng)發(fā)生變化,希望今后依然立于不敗之地的語言同樣需要適應(yīng)這種變化。20世紀(jì)90年代中期,在Java剛問世時,摩爾定律1仍然被奉為金科玉律。人們只需等上幾年,計算機的運行速度就會提高一倍。1由仙童半導(dǎo)體公司(FairchildSemiconductor)和英特爾聯(lián)合創(chuàng)始人戈登?摩爾(GordonMoore)提出。根據(jù)觀察,大約每18個月,封裝到集成電路中的晶體管數(shù)量將增加一倍。參見維基百科的“摩爾定律”詞條。如今,硬件不再依賴于通過增加芯片密度來提高速度。相反,連手機都已大部分配備了多核處理器,這意味著編寫的軟件需要具備在多處理器環(huán)境下運行的能力。函數(shù)式編程強調(diào)“純”函數(shù)(基于相同的輸入將返回相同的結(jié)果,無副作用存在)以及不可變性,從而簡化了并行環(huán)境下的編程。如果不引入任何共享、可變的狀態(tài),且程序可以被分解為若干簡單函數(shù)的集合,就更容易理解并預(yù)測程序的行為。不過,本書并非一本介紹Haskell、Erlang、Frege或任何其他函數(shù)式編程語言的教程,而是側(cè)重于探討Java以及它所引入的函數(shù)式概念。從本質(zhì)上講,Java仍然是一門面向?qū)ο蟮恼Z言。Java目前支持lambda表達式,它本質(zhì)上是被視為一類對象(first-classobject)的方法。Java還增加了方法引用,允許在任何需要lambda表達式的場合使用現(xiàn)有的方法。為利用lambda表達式和方法引用,Java引入了流模型。流模型生成元素并通過流水線(pipeline)進行傳遞和過濾,無須修改原始源代碼。本章的范例將介紹lambda表達式、方法引用與函數(shù)式接口(functionalinterface)的基本語法,并討論接口中的默認方法和靜態(tài)方法。有關(guān)流的詳細討論,參見第3章。1.1lambda表達式問題用戶希望在代碼中使用lambda表達式。方案使用某種lambda表達式語法,并將結(jié)果賦給函數(shù)式接口類型的引用。討論函數(shù)式接口是一種包含單一抽象方法(singleabstractmethod)的接口。類通過為接口中的所有方法提供實現(xiàn)來實現(xiàn)任何接口,這可以通過頂級類(top-levelclass)、內(nèi)部類甚至匿名內(nèi)部類完成。以Runnable接口為例,它從Java1.0開始就已存在。該接口包含的單一抽象方法是run,它不傳入任何參數(shù)并返回void。Thread類構(gòu)造函數(shù)傳入Runnable作為參數(shù),例1-1顯示了Runnable接口的匿名內(nèi)部類實現(xiàn)。例1-1Runnable接口的匿名內(nèi)部類實現(xiàn)publicclassRunnableDemo{publicstaticvoidmain(String[]args){newThread(newRunnable(){?@Overridepublicvoidrun(){System.out.println("insiderunnableusingananonymousinnerclass");}}).start();}}?匿名內(nèi)部類匿名內(nèi)部類語法以關(guān)鍵字new開頭,后面跟著Runnable接口名以及英文小括號,表示定義一個實現(xiàn)該接口但沒有顯式名(explicitname)的類。大括號({})中的代碼重寫run方法,將字符串打印到控制臺。例1-2中的代碼采用lambda表達式,對例1-1進行了改寫。例1-2在Thread構(gòu)造函數(shù)中使用lambda表達式newThread(()->System.out.println("insideThreadconstructorusinglambda")).start();上述代碼使用箭頭將參數(shù)與函數(shù)體隔開(由于沒有參數(shù),這里只使用一對空括號)??梢钥吹?,函數(shù)體只包含一行代碼,所以不需要大括號。這種語法被稱為lambda表達式。注意,任何表達式求值都會自動返回。在本例中,由于println方法返回的是void,所以該表達式同樣會返回void,與run方法的返回類型相匹配。lambda表達式必須匹配接口中單一抽象方法簽名的參數(shù)類型和返回類型,這被稱為與方法簽名兼容。因此,lambda表達式屬于接口方法的實現(xiàn),可以將其賦給該接口類型的引用。例1-3顯示了賦給某個變量的lambda表達式。例1-3將lambda表達式賦給變量Runnabler=()->System.out.println("lambdaexpressionimplementingtherunmethod");newThread(r).start();Java庫中不存在名為Lambda的類,lambda表達式只能被賦給函數(shù)式接口引用?!皩ambda表達式賦給函數(shù)式接口”與“l(fā)ambda表達式屬于函數(shù)式接口中單一抽象方法的實現(xiàn)”表示相同的含義。我們可以將lambda表達式視為實現(xiàn)接口的匿名內(nèi)部類的主體。這就是lambda表達式必須與抽象方法兼容的原因,其參數(shù)類型和返回類型必須匹配該方法的簽名。注意,所實現(xiàn)方法的名稱并不重要,它不會作為lambda表達式語法的一部分出現(xiàn)在代碼中。因為run方法不傳入?yún)?shù),并且返回void,所以本例特別簡單。函數(shù)式接口java.io.FilenameFilter從Java1.0開始就是Java標(biāo)準(zhǔn)庫的一部分,該接口的實例被用作File.list方法的參數(shù),只有滿足該方法的文件才會被返回。根據(jù)Javadoc的描述,F(xiàn)ilenameFilter接口包含單一抽象方法accept,它的簽名如下:booleanaccept(Filedir,Stringname)其中,F(xiàn)ile參數(shù)用于指定文件所在的目錄,String用于指定文件名。例1-4采用匿名內(nèi)部類來實現(xiàn)FilenameFilter接口,只返回Java源文件。例1-4FilenameFilter的匿名內(nèi)部類實現(xiàn)Filedirectory=newFile("./src/main/java");String[]names=directory.list(newFilenameFilter(){?@Overridepublicbooleanaccept(Filedir,Stringname){returnname.endsWith(".java");}});System.out.println(Arrays.asList(names));?匿名內(nèi)部類在例1-4中,如果文件名以.java結(jié)尾,accept方法將返回true,否則返回false。而例1-5采用lambda表達式實現(xiàn)FilenameFilter接口。例1-5FilenameFilter接口的lambda表達式實現(xiàn)Filedirectory=newFile("./src/main/java");String[]names=directory.list((dir,name)->name.endsWith(".java"));?System.out.println(Arrays.asList(names));}?lambda表達式可以看到,代碼要簡單得多。參數(shù)包含在小括號中,但并未聲明類型。在編譯時,編譯器發(fā)現(xiàn)list方法傳入一個FilenameFilter類型的參數(shù),從而獲知其單一抽象方法accept的簽名,進而了解accept的參數(shù)為File和String,因此兼容的lambda表達式參數(shù)必須匹配這些類型。由于accept方法的返回類型是布爾值,所以箭頭右側(cè)的表達式也必須返回布爾值。如例1-6所示,我們也可以在代碼中指定數(shù)據(jù)類型。例1-6具有顯式數(shù)據(jù)類型的lambda表達式Filedirectory=newFile("./src/main/java");String[]names=directory.list((Filedir,Stringname)->?name.endsWith(".java"));?顯式數(shù)據(jù)類型此外,如果lambda表達式的實現(xiàn)多于一行,則需要使用大括號和顯式返回語句,如例1-7所示。例1-7lambda代碼塊Filedirectory=newFile("./src/main/java");String[]names=directory.list((Filedir,Stringname)->{?returnname.endsWith(".java");});System.out.println(Arrays.asList(names));?代碼塊語法這就是lambda代碼塊(blocklambda)。在本例中,雖然代碼主體只有一行,但可以使用大括號將多個語句括起來。注意,不能省略return關(guān)鍵字。lambda表達式在任何情況下都不能脫離上下文存在,上下文指定了將表達式賦給哪個函數(shù)式接口。lambda表達式既可以是方法的參數(shù),也可以是方法的返回類型,還可以被賦給引用。無論哪種情況,賦值類型必須為函數(shù)式接口。1.2方法引用問題用戶希望使用方法引用來訪問某個現(xiàn)有的方法,并將其作為lambda表達式進行處理。方案使用雙冒號表示法(::)將實例引用或類名與方法分開。討論如果說lambda表達式本質(zhì)上是將方法作為對象進行處理,那么方法引用就是將現(xiàn)有方法作為lambda表達式進行處理。例如,java.lang.Iterable接口的forEach方法傳入Consumer作為參數(shù)。如例1-8所示,Consumer可以作為lambda表達式或方法引用來實現(xiàn)。例1-8利用方法引用訪問println方法Stream.of(3,1,4,1,5,9).forEach(x->System.out.println(x));?Stream.of(3,1,4,1,5,9).forEach(System.out::println);?Consumer<Integer>printer=System.out::println;?Stream.of(3,1,4,1,5,9).forEach(printer);?使用lambda表達式?使用方法引用?將方法引用賦給函數(shù)式接口雙冒號表示法在System.out實例上提供了對println方法的引用,它屬于PrintStream類型的引用。方法引用的末尾無須括號。在本例中,程序?qū)⒘鞯乃性卮蛴〉綐?biāo)準(zhǔn)輸出。22討論lambda表達式或方法引用時,很難不涉及流,第3章將專門討論流。目前可以這樣理解:流依次產(chǎn)生一系列元素,但不會將它們存儲在任何位置,也不會對原始源進行修改。如果編寫一個只有一行的lambda表達式來調(diào)用方法,不妨考慮改用等價的方法引用。與lambda語法相比,方法引用具有幾個(不那么顯著的)優(yōu)點。首先,方法引用往往更短。其次,方法引用通常包括含有該方法的類的名稱。這兩點使得代碼更易于閱讀。如例1-9所示,方法引用也可以和靜態(tài)方法一起使用。例1-9在靜態(tài)方法中使用方法引用Stream.generate(Math::random)?.limit(10).forEach(System.out::println);??靜態(tài)方法?實例方法Stream接口定義的generate方法傳入Supplier作為參數(shù)。Supplier是一個函數(shù)式接口,其單一抽象方法get不傳入任何參數(shù)且只生成一個結(jié)果。Math類的random方法與get方法的簽名相互兼容,因為random方法同樣不傳入任何參數(shù),且產(chǎn)生一個0到1之間、均勻分布的雙精度偽隨機數(shù)。方法引用Math::random表示該方法是Supplier接口的實現(xiàn)。由于Stream.generate方法產(chǎn)生的是一個無限流(infinitestream),我們通過limit方法限定只生成10個值,然后使用方法引用System.out::println將這些值打印到標(biāo)準(zhǔn)輸出,作為Consumer的實現(xiàn)。語法方法引用包括以下三種形式,其中一種存在一定的誤導(dǎo)性。object::instanceMethod引用特定對象的實例方法,如System.out::println。Class::staticMethod引用靜態(tài)方法,如Math::max。Class::instanceMethod調(diào)用特定類型的任意對象的實例方法,如String::length。最后一種形式或許令人困惑,因為在Java開發(fā)中,一般只通過類名來調(diào)用靜態(tài)方法。請記住,lambda表達式和方法引用在任何情況下都不能脫離上下文存在。以對象引用為例,上下文提供了方法的參數(shù)。對于System.out::println,等效的lambda表達式為(如例1-8中的上下文所示)://相當(dāng)于System.out::printlnx->System.out.println(x)上下文提供了x的值,它被用作方法的參數(shù)。靜態(tài)方法max與之類似://相當(dāng)于Math::max(x,y)->Math.max(x,y)此時,上下文需要提供兩個參數(shù),lambda表達式返回較大的參數(shù)?!巴ㄟ^類名來調(diào)用實例方法”語法的解釋有所不同,其等效的lambda表達式為://相當(dāng)于String::lengthx->x.length()此時,當(dāng)上下文提供x的值時,它將用作方法的目標(biāo)而非參數(shù)。如果通過類名引用一個傳入多個參數(shù)的方法,則上下文提供的第一個元素將作為方法的目標(biāo),其他元素作為方法的參數(shù)。例1-10顯示了相應(yīng)的代碼。例1-10從類引用(classreference)調(diào)用多參數(shù)實例方法List<String>strings=Arrays.asList("this","is","a","list","of","strings");List<String>sorted=strings.stream().sorted((s1,s2)->pareTo(s2))?.collect(Collectors.toList());List<String>sorted=strings.stream().sorted(String::compareTo)?.collect(Collectors.toList());?方法引用及其等效的lambda表達式Stream接口定義的sorted方法傳入Comparator<T>作為參數(shù),其單一抽象方法為intcompare(Stringother)。sorted方法將每對字符串提供給比較器,并根據(jù)返回整數(shù)的符號對它們進行排序。在本例中,上下文是一對字符串。方法引用語法(采用類名String)調(diào)用第一個元素(lambda表達式中的s1)的compareTo方法,并使用第二個元素s2作為該方法的參數(shù)。在流處理中,如果需要處理一系列輸入,則會頻繁使用方法引用中的類名來訪問實例方法。例1-11顯示了對流中各個String調(diào)用length方法。例1-11使用方法引用在String上調(diào)用length方法Stream.of("this","is","a","stream","of","strings").map(String::length)?.forEach(System.out::println);??通過類名訪問實例方法?通過對象引用訪問實例方法程序調(diào)用length方法將每個字符串轉(zhuǎn)換為一個整數(shù),然后打印所有結(jié)果。方法引用本質(zhì)上屬于lambda表達式的一種簡化語法。lambda表達式在實際中更常見,因為每個方法引用都存在一個等效的lambda表達式,反之則不然。對于例1-11中的方法引用,其等效的lambda表達式如例1-12所示。例1-12方法引用的等效lambda表達式Stream.of("this","is","a","stream","of","strings").map(s->s.length()).forEach(x->System.out.println(x));對任何lambda表達式來說,上下文都很重要。為避免歧義,不妨在方法引用的左側(cè)使用this或super。另見用戶也可以使用方法引用語法來調(diào)用構(gòu)造函數(shù),相關(guān)討論參見范例1.3。第2章將討論java.util.function包以及本范例中出現(xiàn)的Supplier接口。1.3構(gòu)造函數(shù)引用問題用戶希望將方法引用作為流的流水線(streampipeline)的一部分,以實例化某個對象。方案在方法引用中使用new關(guān)鍵字。討論在討論Java8引入的新語法時,通常會提及l(fā)ambda表達式、方法引用以及流。例如,我們希望將一份人員列表轉(zhuǎn)換為相應(yīng)的姓名列表。例1-13的代碼段顯示了解決這個問題的兩種方案。例1-13將人員列表轉(zhuǎn)換為姓名列表List<String>names=people.stream().map(person->person.getName())?.collect(Collectors.toList());//或者采用以下方案List<String>names=people.stream().map(Person::getName)?.collect(Collectors.toList());?lambda表達式?方法引用那么,是否存在其他解決方案呢?如何根據(jù)字符串列表來創(chuàng)建相應(yīng)的Person引用列表呢?盡管仍然可以使用方法引用,不過這次我們改用關(guān)鍵字new,這種語法稱為構(gòu)造函數(shù)引用(constructorreference)。為了說明構(gòu)造函數(shù)引用的用法,我們首先創(chuàng)建一個Person類,它是最簡單的Java對象(POJO)。Person類的唯一作用是包裝一個名為name的簡單字符串特性,如例1-14所示。例1-14Person類publicclassPerson{privateStringname;publicPerson(){}publicPerson(Stringname){=name;}//getter和setter//equals、hashCode與toString方法}如例1-15所示,給定一個字符串集合,通過lambda表達式或構(gòu)造函數(shù)引用,可以將其中的每個字符串映射到Person類。例1-15將字符串轉(zhuǎn)換為Person實例List<String>names=Arrays.asList("GraceHopper","BarbaraLiskov","AdaLovelace","KarenSp?rckJones");List<Person>people=names.stream().map(name->newPerson(name))?.collect(Collectors.toList());//或采用以下方案List<Person>people=names.stream().map(Person::new)?.collect(Collectors.toList());?使用lambda表達式來調(diào)用構(gòu)造函數(shù)?使用構(gòu)造函數(shù)引用來實例化PersonPerson::new的作用是引用Person類中的構(gòu)造函數(shù)。與所有l(wèi)ambda表達式類似,由上下文決定執(zhí)行哪個構(gòu)造函數(shù)。由于上下文提供了一個字符串,使用單參數(shù)的String構(gòu)造函數(shù)。復(fù)制構(gòu)造函數(shù)復(fù)制構(gòu)造函數(shù)(copyconstructor)傳入一個Person參數(shù),并返回一個具有相同特性的新Person,如例1-16所示。例1-16Person的復(fù)制構(gòu)造函數(shù)publicPerson(Personp){=;}如果需要將流代碼從原始實例中分離出來,復(fù)制構(gòu)造函數(shù)將很有用。假設(shè)我們有一個人員列表,先將其轉(zhuǎn)換為流,再轉(zhuǎn)換回列表,那么引用不會發(fā)生變化,如例1-17所示。例1-17將列表轉(zhuǎn)換為流,再轉(zhuǎn)換回列表Personbefore=newPerson("GraceHopper");List<Person>people=Stream.of(before).collect(Collectors.toList());Personafter=people.get(0);assertTrue(before==after);?before.setName("GraceMurrayHopper");?assertEquals("GraceMurrayHopper",after.getName());??對象相同?使用before引用修改人名?after引用中的人名已被修改如例1-18所示,可以通過復(fù)制構(gòu)造函數(shù)來切斷連接。例1-18使用復(fù)制構(gòu)造函數(shù)people=Stream.of(before).map(Person::new)?.collect(Collectors.toList());after=people.get(0);assertFalse(before==after);?assertEquals(before,after);?before.setName("RearAdmiralDr.GraceMurrayHopper");assertFalse(before.equals(after));?使用復(fù)制構(gòu)造函數(shù)?對象不同?但二者是等效的可以看到,當(dāng)調(diào)用map方法時,上下文是Person實例的流。因此,Person::new調(diào)用構(gòu)造函數(shù),它傳入一個Person實例并返回一個等效的新實例,同時切斷了before和after引用之間的連接。3可變參數(shù)構(gòu)造函數(shù)接下來,我們?yōu)镻ersonPOJO添加一個可變參數(shù)構(gòu)造函數(shù)(varargsconstructor),如例1-19所示。例1-19構(gòu)造函數(shù)Person傳入String的可變參數(shù)列表publicPerson(String...names){=Arrays.stream(names).collect(Collectors.joining(""));}上述構(gòu)造函數(shù)傳入零個或多個字符串參數(shù),并使用空格作為定界符將這些參數(shù)拼接在一起。那么,如何調(diào)用這個構(gòu)造函數(shù)呢?任何傳入零個或多個字符串參數(shù)(由逗號隔開)的客戶端都會調(diào)用這個構(gòu)造函數(shù)。一種方案是利用String類定義的split方法,它傳入一個定界符并返回一個String數(shù)組。String[]split(Stringdelimiter)因此,例1-20中的代碼將列表中的每個字符串拆分為單個單詞,并調(diào)用可變參數(shù)構(gòu)造函數(shù)。例1-20可變參數(shù)構(gòu)造函數(shù)的應(yīng)用names.stream()?.map(name->name.split(""))?.map(Person::new)?.collect(Collectors.toList());??創(chuàng)建字符串流?映射到字符串?dāng)?shù)組流?映射到Person流?收集到Person列表在本例中,map方法的上下文包含Person::new構(gòu)造函數(shù)引用,它是一個字符串?dāng)?shù)組流,因此將調(diào)用可變參數(shù)構(gòu)造函數(shù)。如果為該構(gòu)造函數(shù)添加一個簡單的打印語句:System.out.println("Varargsctor,names="+Arrays.asList(names));則輸出如下結(jié)果:Varargsctor,names=[Grace,Hopper]Varargsctor,names=[Barbara,Liskov]Varargsctor,names=[Ada,Lovelace]Varargsctor,names=[Karen,Sp?rck,Jones]數(shù)組構(gòu)造函數(shù)引用也可以和數(shù)組一起使用。如果希望采用Person實例的數(shù)組(Person[])而非列表,可以使用Stream接口定義的toArray方法,它的簽名為:<A>A[]toArray(IntFunction<A[]>generator)toArray方法采用A表示返回數(shù)組的泛型類型(generictype)。數(shù)組包含流的元素,由所提供的generator函數(shù)創(chuàng)建。我們甚至還能使用構(gòu)造函數(shù)引用,如例1-21所示。例1-21創(chuàng)建Person引用的數(shù)組Person[]people=names.stream().map(Person::new)?.toArray(Person[]::new);??Person的構(gòu)造函數(shù)引用?Person數(shù)組的構(gòu)造函數(shù)引用toArray方法參數(shù)創(chuàng)建了一個大小合適的Person引用數(shù)組,并采用經(jīng)過實例化的Person實例進行填充。構(gòu)造函數(shù)引用其實是方法引用的別稱,通過關(guān)鍵字new調(diào)用構(gòu)造函數(shù)。同樣,由上下文決定調(diào)用哪個構(gòu)造函數(shù)。在處理流時,構(gòu)造函數(shù)引用可以提供很大的靈活性。3需要說明的是,將葛麗絲?霍普(GraceHopper)將軍作為本書代碼中的“對象”絕無冒犯之意。作者深信,盡管霍普將軍已于1992年去世,她的水平仍然是作者所無法企及的。(葛麗絲?霍普是美國海軍準(zhǔn)將,也是全球最早的程序員之一?;羝臻_發(fā)了COBOL語言,被譽為“COBOL之母”,計算機術(shù)語bug和debug也是由霍普團隊首先使用并流傳開來的?!g者注)另見有關(guān)方法引用的討論,參見范例1.2。1.4函數(shù)式接口問題用戶希望使用現(xiàn)有的函數(shù)式接口,或編寫自定義函數(shù)式接口。方案創(chuàng)建只包含單一抽象方法的接口,并為其添加@FunctionalInterface注解。討論Java8引入的函數(shù)式接口是一種包含單一抽象方法的接口,因此可以作為lambda表達式或方法引用的目標(biāo)。關(guān)鍵字abstract在這里很重要。在Java8之前,接口中的所有方法被默認視為抽象方法,不需要為它們添加abstract。我們定義一個名為PalindromeChecker(回文檢查器)的接口,如例1-22所示。例1-22回文檢查器接口@FunctionalInterfacepublicinterfacePalindromeChecker{booleanisPalidrome(Strings);}由于接口中的所有方法均為public方法4,可以省略訪問修飾符,如同省略abstract關(guān)鍵字一樣。4至少在Java9之前,接口中也允許使用private方法。更多詳細信息,參見范例10.2。由于PalindromeChecker僅包含一個抽象方法,它屬于函數(shù)式接口。Java8在java.lang包中提供了@FunctionalInterface注解,可以應(yīng)用到函數(shù)式接口,如例1-22所示。@FunctionalInterface注解并非必需,但使用它是一種好習(xí)慣,原因有兩點。首先,@FunctionalInterface注解會觸發(fā)編譯時校驗(compile-timecheck),有助于確保接口符合要求。如果接口不包含或包含多個抽象方法,程序?qū)⑻崾揪幾g錯誤。其次,添加@FunctionalInterface注解后,會在Javadoc中生成以下語句:FunctionalInterface:Thisisafunctionalinterfaceandcanthereforebeusedastheassignmenttargetforalambdaexpressionormethodreference.函數(shù)式接口中同樣可以使用default和static方法。由于這兩種方法都有相應(yīng)的實現(xiàn),它們與“僅包含一個抽象方法”的要求并不矛盾。示例代碼如例1-23所示。例1-23MyInterface是一個包含靜態(tài)方法和默認方法的函數(shù)式接口@FunctionalInterfacepublicinterfaceMyInterface{intmyMethod();?//intmyOtherMethod();?defaultStringsayHello(){return"Hello,World!";}staticvoidmyStaticMethod(){System.out.println("I'mastaticmethodinaninterface");}}?單一抽象方法?如果這條語句未被注釋掉,MyInterface將不再是函數(shù)式接口可以看到,如果myOtherMethod方法未被注釋掉,MyInterface就不再滿足函數(shù)式接口的要求,@FunctionalInterface注解將報錯:“存在多個非重寫的抽象方法?!苯涌诳梢岳^承其他接口(甚至不止一個)。@FunctionalInterface注解將對當(dāng)前接口進行校驗。因此,如果一個接口繼承現(xiàn)有的函數(shù)式接口后,又添加了其他抽象方法,該接口就不再是函數(shù)式接口,如例1-24所示。例1-24繼承函數(shù)式接口的MyChildInterface不再屬于函數(shù)式接口publicinterfaceMyChildInterfaceextendsMyInterface{intanotherMethod();?}?其他抽象方法MyChildInterface不屬于函數(shù)式接口,因為它包含兩個抽象方法:繼承自MyInterface的myMethod和聲明的anotherMethod。即便沒有添加@FunctionalInterface注解,代碼也可以編譯,因為MyChildInterface就是一個標(biāo)準(zhǔn)接口,但無法作為lambda表達式的目標(biāo)。此外,還有一種不太常見的情況值得注意。Comparator接口用于排序,其他范例將對此進行討論。查看Comparator接口的Javadoc信息,并點擊AbstractMethods(抽象方法)標(biāo)簽后,將顯示以下信息(圖1-1)。圖1-1:Comparator接口包含的抽象方法可以看到,Comparator接口包含兩種抽象方法,且其中一種方法是在java.lang.Object類中實際實現(xiàn)的,那么Comparator為什么還屬于函數(shù)式接口呢?這里的特別之處在于,圖中顯示的equals方法來自O(shè)bject類,因此已有一個默認的實現(xiàn)。根據(jù)Javadoc的描述,出于性能方面的考慮,用戶可以提供滿足相同契約(interfacecontract)的自定義equals方法,但不對該方法進行重寫“始終是安全的”。根據(jù)函數(shù)式接口的定義,Object類中的方法與單一抽象方法的要求并不矛盾,因此Comparator仍然屬于函數(shù)式接口。另見接口中默認方法的相關(guān)應(yīng)用,參見范例1.5。接口中靜態(tài)方法的相關(guān)應(yīng)用,參見范例1.6。1.5接口中的默認方法問題用戶希望在接口中提供方法的實現(xiàn)。方案將接口方法聲明為default,并以常規(guī)方式添加實現(xiàn)。討論Java之所以不支持多繼承(multipleinheritance),是為了避免所謂的鉆石問題(diamondproblem)??紤]如圖1-2所示的繼承層次結(jié)構(gòu)(有點類似UML)。圖1-2:Animal繼承Animal類包括Bird和Horse兩個子類,二者重寫了Animal的speak方法:Horse是“嘶嘶”(whinny),而Bird是“唧唧”(chirp)。那么Pegasus(從Horse和Bird繼承而來)5呢?如果將Animal類型的引用賦給Pegasus的實例會怎樣?speak方法又該返回什么呢?5“一匹長有雙翼的駿馬?!保ㄔ醋缘鲜磕犭娪啊洞罅κ亢8窳λ埂罚悴粫]聽說過希臘神話和海格力斯吧?)Animalanimal=newPegaus();animal.speak();//嘶嘶、唧唧還是其他聲音?不同語言處理這個問題的方法各不相同。例如,C++支持多繼承,但如果某個類繼承了相互沖突的實現(xiàn)則不會被編譯。6而在Eiffel7中,編譯器允許用戶選擇所需的實現(xiàn)。6但仍然可以使用虛繼承(virtualinheritance)來解決這個問題。7Eiffel或許對讀者來說略顯晦澀,它是面向?qū)ο缶幊痰幕A(chǔ)語言之一。感興趣的話,可以參考BertrandMeyer撰寫的Object-OrientedSoftwareConstruction,SecondEdition,該書由PrenticeHall于1997年出版。Java禁止多繼承。為避免一個類與多種類型都具有“某種”關(guān)系,Java引入接口作為解決方案。由于接口只包含抽象方法,不會存在相互沖突的實現(xiàn)。接口之所以允許多繼承,是因為只有方法簽名被繼承。問題在于,如果永遠無法在接口中實現(xiàn)方法,就會導(dǎo)致一些奇怪的情況出現(xiàn)。以java.util.Collection接口為例,它定義了以下方法:booleanisEmpty()intsize()如果集合中沒有元素,isEmpty方法將返回true,否則返回false。而size方法返回集合中元素的數(shù)量。如例1-25所示,無論底層實現(xiàn)如何,都可以根據(jù)size立即實現(xiàn)isEmpty方法。例1-25根據(jù)size實現(xiàn)isEmpty方法publicbooleanisEmpty(){returnsize()==0;}由于Collection是一個接口,不能對它進行這樣的處理,但可以使用Java標(biāo)準(zhǔn)庫提供的java.util.AbstractCollection類。它是一個抽象類,所包含的isEmpty方法與本例中isEmpty的實現(xiàn)完全相同。如果用戶正在創(chuàng)建自定義的集合實現(xiàn)(collectionimplementation)且還沒有超類,可以通過繼承AbstractCollection類來獲得isEmpty方法。不過如果已有超類,就必須改為實現(xiàn)Collection接口,且不要忘記提供自定義的isEmpty和size實現(xiàn)。這些對經(jīng)驗豐富的Java開發(fā)人員而言很容易,但從Java8開始,情況有所改變。目前只須將某個方法聲明為default并提供一個實現(xiàn),就能為接口方法添加實現(xiàn)。如例1-26所示,Employee接口包含兩種抽象方法和一種默認方法。例1-26Employee接口包含默認方法publicinterfaceEmployee{StringgetFirst();StringgetLast();voidconvertCaffeineToCodeForMoney();defaultStringgetName(){?returnString.format("%s%s",getFirst(),getLast());}}?具有實現(xiàn)的默認方法getName方法由關(guān)鍵字default聲明,其實現(xiàn)取決于Employee接口的另外兩種抽象方法,即getFirst和getLast。為保持向后兼容性,Java的許多現(xiàn)有接口都采用默認方法進行了增強。一般而言,為接口添加新方法會破壞所有現(xiàn)有的實現(xiàn)。如果添加的新方法被聲明為默認方法,則所有現(xiàn)有的實現(xiàn)將繼承新方法且仍然有效。這使得庫維護者可以在JDK中添加新的默認方法,而不會破壞現(xiàn)有的實現(xiàn)。例如,java.util.Collection接口目前包含以下默認方法:defaultbooleanremoveIf(Predicate<?superE>filter)defaultStream<E>stream()defaultStream<E>parallelStream()defaultSpliterator<E>spliterator()removeIf方法將刪除集合中所有滿足Predicate8參數(shù)的元素,如果刪除了任何元素,該方法將返回true。stream和parallelStream方法用于創(chuàng)建流,二者屬于工廠方法。spliterator方法從實現(xiàn)Spliterator接口的類中返回一個對象,它對來自源的元素進行遍歷和分區(qū)。8Predicate是java.util.function包新增的一種函數(shù)式接口,相關(guān)討論請參見范例2.3。如例1-27所示,默認方法與其他方法的用法并無二致。例1-27默認方法的應(yīng)用List<Integer>nums=newArrayList<>();nums.add(-3);nums.add(1);nums.add(4);nums.add(-1);nums.add(5);nums.add(9);booleanremoved=nums.removeIf(n->n<=0);?System.out.println("Elementswere"+(removed?"":"NOT")+"removed");nums.forEach(System.out::println);??使用Collection接口定義的默認方法removeIf?使用Iterator接口定義的默認方法forEach如果一個類采用同一種默認方法實現(xiàn)了兩個接口,會出現(xiàn)什么情況呢?范例5.5將討論這個問題,不過簡而言之,類可以實現(xiàn)方法本身。詳細信息請參見范例5.5。另見范例5.5將討論一個類采用默認方法實現(xiàn)多個接口時需要遵守的規(guī)則。1.6接口中的靜態(tài)方法問題用戶希望為接口添加一個類級別(classlevel)的工具方法和相應(yīng)的實現(xiàn)。方案將接口方法聲明為static,并以常規(guī)方式添加實現(xiàn)。討論Java類的靜態(tài)成員是類級別的。換言之,靜態(tài)成員與整個類相關(guān)聯(lián),而不是與特定的實例相關(guān)聯(lián)。但從設(shè)計的角度看,靜態(tài)成員在接口中的使用是有問題的,舉例如下。當(dāng)多個不同的類實現(xiàn)接口時,類級別成員指的是什么?類是否需要通過實現(xiàn)接口來使用靜態(tài)方法?類中的靜態(tài)方法是通過類名訪問的。如果類實現(xiàn)了一個接口,那么靜態(tài)方法是通過類名還是接口名來調(diào)用呢?為解決這些問題,Java開發(fā)團隊嘗試了幾種不同的方案。在Java8之前,接口完全不支持使用靜態(tài)成員,不過這導(dǎo)致了工具類的產(chǎn)生,這是一種只包含靜態(tài)方法的類。java.util.Collections就是一種典型的工具類,它不僅包括用于排序和搜索的方法,也定義了采用同步或不可修改的類型包裝集合的方法。在NIO包中,另一種工具類是java.nio.file.Paths,它只包括從字符串或URI中解析Path實例的兩個靜態(tài)方法。而在Java8中,我們可以隨時為接口添加靜態(tài)方法,步驟如下。為方法添加static關(guān)鍵字。提供一種無法被重寫的實現(xiàn)。此時,靜態(tài)方法類似于默認方法,包含在Javadoc的DefaultMethods(默認標(biāo)簽)中。通過接口名訪問方法。類不需要通過實現(xiàn)接口來使用靜態(tài)方法。java.util.Comparator接口定義的comparing方法就是一種實用的靜態(tài)方法,它包括comparingInt、comparingLong、comparingDouble等三種基本變體。此外,Comparator接口還包括naturalOrder和reverseOrder兩種靜態(tài)方法。例1-28給出了這些方法的用法。例1-28字符串排序List<String>bonds=Arrays.asList("Connery","Lazenby","Moore","Dalton","Brosnan","Craig");List<String>sorted=bonds.stream().sorted(Comparator.naturalOrder())?.collect(Collectors.toList());//[Brosnan,Connery,Craig,Dalton,Lazenby,Moore]sorted=bonds.stream().sorted(Comparator.reverseOrder())?.collect(Collectors.toList());//[Moore,Lazenby,Dalton,Craig,Connery,Brosnan]sorted=bonds.stream().sorted(Cparing(String::toLowerCase))?.collect(Collectors.toList());//[Brosnan,Connery,Craig,Dalton,Lazenby,Moore]sorted=bonds.stream().sorted(CparingInt(String::length))?.collect(Collectors.toList());//[Moore,Craig,Dalton,Connery,Lazenby,Brosnan]sorted=bonds.stream().sorted(CparingInt(String::length)?.thenComparing(Comparator.naturalOrder())).collect(Collectors.toList());//[Craig,Moore,Dalton,Brosnan,Connery,Lazenby]?自然順序(字典序)?反向順序(字典序)?按小寫名稱排序?按姓名長度排序?按姓名長度排序,如果長度相同則按字典序排序本例顯示了如何利用Comparator接口提供的靜態(tài)方法對一份演員名單進行排序,名單中出現(xiàn)的演員是這些年來詹姆斯?邦德的扮演者。9有關(guān)比較器的詳細討論,請參見范例4.1。9我差點將伊德瑞斯?艾爾巴(IdrisElba)加入名單,但總算忍住沒這么做。(艾爾巴是英國影星,因在《環(huán)太平洋》《雷神》等影片中飾演的角色為影迷所熟知,2014年曾傳出他將出演下一任邦德的消息——譯者注。)由于接口中有靜態(tài)方法,我們不必創(chuàng)建單獨的工具類。但需要的話,仍然可以創(chuàng)建工具類。請注意以下幾點:靜態(tài)方法必須有一個實現(xiàn)無法重寫靜態(tài)方法通過接口名調(diào)用靜態(tài)方法無須實現(xiàn)接口以使用靜態(tài)方法另見接口中靜態(tài)方法的應(yīng)用貫穿全書,有關(guān)Comparator接口定義的靜態(tài)方法請參見范例4.1。第2章java.util.function包第1章討論了lambda表達式和方法引用的基本語法,二者在任何情況下都不能脫離上下文(context)而存在。lambda表達式和方法引用總是被賦給函數(shù)式接口,它提供了所實現(xiàn)的單一抽象方法的信息。Java標(biāo)準(zhǔn)庫中的許多接口僅包含一個抽象方法,它們屬于函數(shù)式接口。為此,Java8專門定義了java.util.function包,它僅包含可以在庫的其余部分重用的函數(shù)式接口。java.util.function包中的接口分為四類,分別是Consumer(消費型接口)、Supplier(供給型接口)、Predicate(謂詞型接口)以及Function(功能型接口)。Consumer接口傳入一個泛型參數(shù)(genericargument),不返回任何值;Supplier接口不傳入?yún)?shù),返回一個值;Predicate接口傳入一個參數(shù),返回一個布爾值;Function接口傳入一個參數(shù),返回一個值。每種基本接口還包含若干相關(guān)的接口。以Consumer接口為例,用于處理基本數(shù)據(jù)類型的是IntConsumer、LongConsumer和DoubleConsumer接口,BiConsumer接口傳入兩個參數(shù)并返回void。雖然根據(jù)定義,這一章討論的函數(shù)式接口只包含一個抽象方法,但大部分接口也包含聲明為static或default的方法。對于開發(fā)人員而言,掌握這些方法有助于提高工作效率。2.1Consumer接口問題用戶希望編寫實現(xiàn)java.util.function.Consumer包的lambda表達式。方案使用lambda表達式或方法引用來實現(xiàn)voidaccept(Tt)方法。討論例2-1列出了Consumer接口定義的方法,其單一抽象方法為voidaccept(Tt)。例2-1Consumer接口定義的方法voidaccept(Tt)?defaultConsumer<T>andThen(Consumer<?superT>after)??單一抽象方法?用于復(fù)合操作的默認方法accept方法傳入一個泛型參數(shù)并返回void。在所有傳入Consumer作為參數(shù)的方法中,最常見的是java.util.Iterable接口的默認forEach方法,如例2-2所示。例2-2Iterable接口定義的forEach方法defaultvoidforEach(Consumer<?superT>action)??將可迭代集合(iterablecollection)中的所有元素傳遞給Consumer參數(shù)如例2-3所示,所有線性集合通過對集合中每個元素執(zhí)行給定的操作來實現(xiàn)Iterable接口。例2-3打印集合中的元素List<String>strings=Arrays.asList("this","is","a","list","of","strings");strings.forEach(newConsumer<String>(){?@Overridepublicvoidaccept(Strings){System.out.println(s);}});strings.forEach(s->System.out.println(s));?strings.forEach(System.out::println);??匿名內(nèi)部類實現(xiàn)?lambda表達式?方法引用在本例中,由于accept方法只傳入一個參數(shù)且不返回任何值,其簽名與lambda表達式相符。通過System.out訪問PrintStream類的println方法,它與Consumer相互兼容。因此,最后兩條語句都可以作為Consumer參數(shù)的目標(biāo)。如表2-1所示,java.util.function包還定義了三種Consumer<T>的基本變體,以及一種雙參數(shù)形式。表2-1:其他Consumer接口接口單一抽象方法IntConsumervoidaccept(intx)DoubleConsumervoidaccept(doublex)LongConsumervoidaccept(longx)BiConsumervoidaccept(Tt,Uu)Consumer接口期望執(zhí)行帶有副作用的操作(即它可能會改變輸入?yún)?shù)的內(nèi)部狀態(tài)),參見范例2.3。BiConsumer接口的accept方法傳入兩個泛型參數(shù),這兩個泛型參數(shù)應(yīng)為不同的類型。java.util.function包定義了BiConsumer接口的三種變體,每種變體的第二個參數(shù)為基本數(shù)據(jù)類型。以O(shè)bjIntConsumer接口為例,其accept方法傳入兩個參數(shù),分別為泛型參數(shù)和int參數(shù)。ObjLongConsumer和ObjDoubleConsumer接口的定義與ObjIntConsumer類似。標(biāo)準(zhǔn)庫還支持Consumer接口的一些其他用法。Optional.ifPresent(Consumer<?superT>consumer)如果值存在,則調(diào)用指定的consumer;否則不進行任何操作。Stream.forEach(Consumer<?superT>action)對流的每個元素執(zhí)行操作1。Stream.forEachOrdered方法與之類似,它根據(jù)元素的出現(xiàn)順序(encounterorder)訪問元素。1這項操作十分常見,因此Iterable接口也直接定義了forEach方法。當(dāng)源元素不是來自集合或需要創(chuàng)建并行流時,Stream.forEach方法就很有用。Stream.peek(Consumer<?superT>action)首先執(zhí)行給定操作,再返回一個與現(xiàn)有流包含相同元素的流。peek方法在調(diào)試中極為有用(參見范例3.5)。另見Consumer接口定義的andThen方法用于函數(shù)復(fù)合(functioncomposition),詳細討論參見范例5.8。有關(guān)Stream.peek方法的討論參見范例3.5。2.2Supplier接口問題用戶希望實現(xiàn)java.util.function.Supplier接口。方案使用lambda表達式或方法引用來實現(xiàn)Tget()方法。討論Supplier接口相當(dāng)簡單,它不包含任何靜態(tài)或默認方法,只有一個抽象方法Tget()。為實現(xiàn)Supplier接口,需要提供一個不傳入?yún)?shù)且返回泛型類型(generictype)的方法。根據(jù)Javadoc的描述,調(diào)用Supplier時,不要求每次都返回一個新的或不同的結(jié)果。Supplier的一種簡單應(yīng)用是Math.random方法,它不傳入?yún)?shù)且返回double型數(shù)據(jù)。如例2-4所示,Math.random方法可以被賦給Supplier引用并隨時調(diào)用。例2-4使用Math.random作為SupplierLoggerlogger=Logger.getLogger("...");DoubleSupplierrandomSupplier=newDoubleSupplier(){?@OverridepublicdoublegetAsDouble(){returnMath.random();}};randomSupplier=()->Math.random();?randomSupplier=Math::random;?(randomSupplier);?匿名內(nèi)部類實現(xiàn)?lambda表達式?方法引用DoubleSupplier接口包含的單一抽象方法為getAsDouble,它返回一個double型數(shù)據(jù)。表2-2列出了java.util.function包定義的其他相關(guān)Supplier接口。表2-2:其他Supplier接口接口單一抽象方法IntSupplierintge
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
- 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責(zé)。
- 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 探索2024年教育風(fēng)向標(biāo):《口耳目》教案
- 2024年:虛擬現(xiàn)實技術(shù)在教育培訓(xùn)中的應(yīng)用
- 人教版初中化學(xué)九年級上冊-各單元測試卷共二十一套及答案
- 石頭的啟示作文3篇
- 甲狀腺功能亢進病例分析培訓(xùn)課件
- (完整版)暗涵清淤專項方案
- 汽車中級工試卷
- 2024-2025學(xué)年新教材高中英語UNIT3FOODANDCULTUREPeriod1課時作業(yè)含解析新人教版選擇性必修第二冊
- 2024-2025學(xué)年高中物理第一章靜電場6電勢差與電場強度的關(guān)系課時作業(yè)含解析新人教版選修3-1
- 2024-2025學(xué)年新教材高中英語Unit4AmazingartSectionⅢDevelopingideas學(xué)案外研版必修第三冊
- 人工智能訓(xùn)練師(中級數(shù)據(jù)標(biāo)注員)理論考試題庫大全(含答案)
- 醫(yī)院科室合作共建方案
- 3.1DNA是主要的遺傳物質(zhì)課件-高一下學(xué)期生物人教版必修二
- 小學(xué)數(shù)學(xué)計算專項訓(xùn)練之乘法分配律(提公因數(shù))
- 《食物在體內(nèi)的旅行》說課稿
- 手機綜合癥小品臺詞
- 校園封閉安全管理制度培訓(xùn)
- 職規(guī)大賽醫(yī)學(xué)影像成長賽道
- 市政工程道路施工主要管理人員及勞動力安排
- 2023年江蘇省事業(yè)單位公開招聘考試真題
- 建筑設(shè)計方法入門(建筑設(shè)計)
評論
0/150
提交評論