摘要:是第一批在堆上分配記錄的類型語言之一。實際上,的這段話低估了過去五十年來數(shù)百萬程序員為修復空引用所耗費的代價。很明顯,這種方式不具備擴展性,同時還犧牲了代碼的可讀性。是目前程序開發(fā)中最典型的異常。完成這一任務有多種方法。
用Optional取代null
如果你作為Java程序員曾經(jīng)遭遇過NullPointerException,請舉起手。如果這是你最常遭遇的異常,請繼續(xù)舉手。非常可惜,這個時刻,我們無法看到對方,但是我相信很多人的手這個時刻是舉著的。我們還猜想你可能也有這樣的想法:“毫無疑問,我承認,對任何一位Java程序員來說,無論是初出茅廬的新人,還是久經(jīng)江湖的專家,NullPointerException都是他心中的痛,可是我們又無能為力,因為這就是我們?yōu)榱耸褂梅奖闵踔敛豢杀苊獾南駈ull引用這樣的構造所付出的代價。”這就是程序設計世界里大家都持有的觀點,然而,這可能并非事實的全部真相,只是我們根深蒂固的一種偏見。
1965年,英國一位名為Tony Hoare的計算機科學家在設計ALGOL W語言時提出了null引用的想法。ALGOL W是第一批在堆上分配記錄的類型語言之一。Hoare選擇null引用這種方式,“只是因為這種方法實現(xiàn)起來非常容易”。雖然他的設計初衷就是要“通過編譯器的自動檢測機制,確保所有使用引用的地方都是絕對安全的”,他還是決定為null引用開個綠燈,因為他認為這是為“不存在的值”建模最容易的方式。很多年后,他開始為自己曾經(jīng)做過這樣的決定而后悔不迭,把它稱為“我價值百萬的重大失誤”。我們已經(jīng)看到它帶來的后果——程序員對對象的字段進行檢查,判斷它的值是否為期望的格式,最終卻發(fā)現(xiàn)我們查看的并不是一個對象,而是一個空指針,它會立即拋出一個讓人厭煩的NullPointerException異常。
實際上,Hoare的這段話低估了過去五十年來數(shù)百萬程序員為修復空引用所耗費的代價。近十年出現(xiàn)的大多數(shù)現(xiàn)代程序設計語言,包括Java,都采用了同樣的設計方式,其原因是為了與更老的語言保持兼容,或者就像Hoare曾經(jīng)陳述的那樣,“僅僅是因為這樣實現(xiàn)起來更加容易”。讓我們從一個簡單的例子入手,看看使用null都有什么樣的問題。
如何為缺失的值建模假設你需要處理下面這樣的嵌套對象,這是一個擁有汽車及汽車保險的客戶。
public class Person { private Car car; public Car getCar() { return car; } } public class Car { private Insurance insurance; public Insurance getInsurance() { return insurance; } } public class Insurance { private String name; public String getName() { return name; } }
那么,下面這段代碼存在怎樣的問題呢?
public String getCarInsuranceName(Person person) { return person.getCar().getInsurance().getName(); }
這段代碼看起來相當正常,但是現(xiàn)實生活中很多人沒有車。所以調(diào)用getCar方法的結果會怎樣呢?在實踐中,一種比較常見的做法是返回一個null引用,表示該值的缺失,即用戶沒有車。而接下來,對getInsurance的調(diào)用會返回null引用的insurance,這會導致運行時出現(xiàn)一個NullPointerException,終止程序的運行。但這還不是全部。如果返回的person值為null會怎樣?如果getInsurance的返回值也是null,結果又會怎樣?
采用防御式檢查減少NullPointerException怎樣做才能避免這種不期而至的NullPointerException呢?通常,你可以在需要的地方添加null的檢查(過于激進的防御式檢查甚至會在不太需要的地方添加檢測代碼),并且添加的方式往往各有不同。下面這個例子是我們試圖在方法中避免NullPointerException的第一次嘗試。
public String getCarInsuranceName(Person person) { if (person != null) { Car car = person.getCar(); if (car != null) { Insurance insurance = car.getInsurance(); if (insurance != null) { return insurance.getName(); } } } return "Unknown"; }
這個方法每次引用一個變量都會做一次null檢查,如果引用鏈上的任何一個遍歷的解變量值為null,它就返回一個值為“Unknown”的字符串。唯一的例外是保險公司的名字,你不需要對它進行檢查,原因很簡單,因為任何一家公司必定有個名字。注意到了嗎,由于你掌握業(yè)務領域的知識,避免了最后這個檢查,但這并不會直接反映在你建模數(shù)據(jù)的Java類之中。
我們將上面的代碼標記為“深層質(zhì)疑”,原因是它不斷重復著一種模式:每次你不確定一個變量是否為null時,都需要添加一個進一步嵌套的if塊,也增加了代碼縮進的層數(shù)。很明顯,這種方式不具備擴展性,同時還犧牲了代碼的可讀性。面對這種窘境,你也許愿意嘗試另一種方案。下面的代碼清單中,我們試圖通過一種不同的方式避免這種問題。
public String getCarInsuranceName(Person person) { if (person == null) { return "Unknown"; } Car car = person.getCar(); if (car == null) { return "Unknown"; } Insurance insurance = car.getInsurance(); if (insurance == null) { return "Unknown"; } return insurance.getName(); }
第二種嘗試中,你試圖避免深層遞歸的if語句塊,采用了一種不同的策略:每次你遭遇null變量,都返回一個字符串常量“Unknown”。然而,這種方案遠非理想,現(xiàn)在這個方法有了四個截然不同的退出點,使得代碼的維護異常艱難。更糟的是,發(fā)生null時返回的默認值,即字符串“Unknown”在三個不同的地方重復出現(xiàn)——出現(xiàn)拼寫錯誤的概率不小!當然,你可能會說,我們可以用把它們抽取到一個常量中的方式避免這種問題。
進一步而言,這種流程是極易出錯的;如果你忘記檢查了那個可能為null的屬性會怎樣?通過這一章的學習,你會了解使用null來表示變量值的缺失是大錯特錯的。你需要更優(yōu)雅的方式來對缺失的變量值建模。
null 帶來的種種問題讓我們一起回顧一下到目前為止進行的討論,在Java程序開發(fā)中使用null會帶來理論和實際操作上的種種問題。
它是錯誤之源。NullPointerException是目前Java程序開發(fā)中最典型的異常。
它會使你的代碼膨脹。它讓你的代碼充斥著深度嵌套的null檢查,代碼的可讀性糟糕透頂。
它自身是毫無意義的。null自身沒有任何的語義,尤其是,它代表的是在靜態(tài)類型語言中以一種錯誤的方式對缺失變量值的建模。
它破壞了Java的哲學。Java一直試圖避免讓程序員意識到指針的存在,唯一的例外是:null指針。
它在Java的類型系統(tǒng)上開了個口子。null并不屬于任何類型,這意味著它可以被賦值給任意引用類型的變量。這會導致問題,原因是當這個變量被傳遞到系統(tǒng)中的另一個部分后,你將無法獲知這個null變量最初的賦值到底是什么類型。
Optional 類入門為了更好的解決和避免NPE異常,Java 8中引入了一個新的類java.util.Optional
變量存在時,Optional類只是對類簡單封裝。變量不存在時,缺失的值會被建模成一個“空”的Optional對象,由方法Optional.empty()返回。Optional.empty()方法是一個靜態(tài)工廠方法,它返回Optional類的特定單一實例。你可能還有疑惑,null引用和Optional.empty()有什么本質(zhì)的區(qū)別嗎?從語義上,你可以把它們當作一回事兒,但是實際中它們之間的差別非常大: 如果你嘗試解引用一個null , 一定會觸發(fā)NullPointerException , 不過使用Optional.empty()就完全沒事兒,它是Optional類的一個有效對象,多種場景都能調(diào)用,非常有用。關于這一點,接下來的部分會詳細介紹。
使用Optional而不是null的一個非常重要而又實際的語義區(qū)別是,第一個例子中,我們在聲明變量時使用的是Optional
牢記上面這些原則,你現(xiàn)在可以使用Optional類對最初的代碼進行重構,結果如下。
public class Person { private Optionalcar; public Optional getCar() { return car; } } public class Insurance { private String name; public String getName() { return name; } } public class Car { private Optional insurance; public Optional getInsurance() { return insurance; } }
發(fā)現(xiàn)Optional是如何豐富你模型的語義了吧。代碼中person引用的是Optional
與此同時,我們看到insurance公司的名稱被聲明成String類型,而不是Optional
在你的代碼中始終如一地使用Optional,能非常清晰地界定出變量值的缺失是結構上的問題,還是你算法上的缺陷,抑或是你數(shù)據(jù)中的問題。另外,我們還想特別強調(diào),引入Optional類的意圖并非要消除每一個null引用。與此相反,它的目標是幫助你更好地設計出普適的API,讓程序員看到方法簽名,就能了解它是否接受一個Optional的值。這種強制會讓你更積極地將變量從Optional中解包出來,直面缺失的變量值。
應用Optional 的幾種模式到目前為止,一切都很順利;你已經(jīng)知道了如何使用Optional類型來聲明你的域模型,也了解了這種方式與直接使用null引用表示變量值的缺失的優(yōu)劣。但是,我們該如何使用呢?用這種方式能做什么,或者怎樣使用Optional封裝的值呢?
創(chuàng)建Optional 對象使用Optional之前,你首先需要學習的是如何創(chuàng)建Optional對象。完成這一任務有多種方法。
聲明一個空的Optional
正如前文已經(jīng)提到,你可以通過靜態(tài)工廠方法Optional.empty,創(chuàng)建一個空的Optional對象:
OptionaloptCar = Optional.empty();
依據(jù)一個非空值創(chuàng)建Optional
你還可以使用靜態(tài)工廠方法Optional.of,依據(jù)一個非空值創(chuàng)建一個Optional對象:
OptionaloptCar = Optional.of(car);
如果car是一個null,這段代碼會立即拋出一個NullPointerException,而不是等到你試圖訪問car的屬性值時才返回一個錯誤。
可接受null的Optional
最后,使用靜態(tài)工廠方法Optional.ofNullable,你可以創(chuàng)建一個允許null值的Optional對象:
OptionaloptCar = Optional.ofNullable(car);
如果car是null,那么得到的Optional對象就是個空對象。
你可能已經(jīng)猜到,我們還需要繼續(xù)研究“如何獲取Optional變量中的值”。尤其是,Optional提供了一個get方法,它能非常精準地完成這項工作,我們在后面會詳細介紹這部分內(nèi)容。不過get方法在遭遇到空的Optional對象時也會拋出異常,所以不按照約定的方式使用它,又會讓我們再度陷入由null引起的代碼維護的夢魘。因此,我們首先從無需顯式檢查的Optional值的使用入手,這些方法與Stream中的某些操作極其相似。
使用map 從Optional 對象中提取和轉換值從對象中提取信息是一種比較常見的模式。比如,你可能想要從insurance公司對象中提取公司的名稱。提取名稱之前,你需要檢查insurance對象是否為null,代碼如下所示:
String name = null; if(insurance != null){ name = insurance.getName(); }
為了支持這種模式,Optional提供了一個map方法。它的工作方式如下:
OptionaloptionalInsurance = Optional.ofNullable(insurance); Optional name = optionalInsurance.map(Insurance::getName);
從概念上,這與我們在第4章和第5章中看到的流的map方法相差無幾。map操作會將提供的函數(shù)應用于流的每個元素。你可以把Optional對象看成一種特殊的集合數(shù)據(jù),它至多包含一個元素。如果Optional包含一個值,那函數(shù)就將該值作為參數(shù)傳遞給map,對該值進行轉換。如果Optional為空,就什么也不做。
這看起來挺有用,但是你怎樣才能應用起來,重構之前的代碼呢?前文的代碼里用安全的方式鏈接了多個方法。
public String getCarInsuranceName(Person person) { return person.getCar().getInsurance().getName(); }
為了達到這個目的,我們需要求助Optional提供的另一個方法flatMap。
使用flatMap 鏈接Optional 對象由于我們剛剛學習了如何使用map,你的第一反應可能是我們可以利用map重寫之前的代碼,如下所示:
OptionaloptPerson = Optional.of(person); Optional name = optPerson.map(Person::getCar) .map(Car::getInsurance) .map(Insurance::getName);
不幸的是,這段代碼無法通過編譯。為什么呢?optPerson是Optional
所以,我們該如何解決這個問題呢?讓我們再回顧一下你剛剛在流上使用過的模式:flatMap方法。使用流時,flatMap方法接受一個函數(shù)作為參數(shù),這個函數(shù)的返回值是另一個流。這個方法會應用到流中的每一個元素,最終形成一個新的流的流。但是flagMap會用流的內(nèi)容替換每個新生成的流。換句話說,由方法生成的各個流會被合并或者扁平化為一個單一的流。這里你希望的結果其實也是類似的,但是你想要的是將兩層的optional合并為一個。
這個例子中,傳遞給流的flatMap方法會將每個正方形轉換為另一個流中的兩個三角形。那么,map操作的結果就包含有三個新的流,每一個流包含兩個三角形,但flatMap方法會將這種兩層的流合并為一個包含六個三角形的單一流。類似地,傳遞給optional的flatMap方法的函數(shù)會將原始包含正方形的optional對象轉換為包含三角形的optional對象。如果將該方法傳遞給map方法,結果會是一個Optional對象,而這個Optional對象中包含了三角形;但flatMap方法會將這種兩層的Optional對象轉換為包含三角形的單一Optional對象。
使用Optional獲取car的保險公司名稱
相信現(xiàn)在你已經(jīng)對Optional的map和flatMap方法有了一定的了解,讓我們看看如何應用。
public String getCarInsuranceName(Optionalperson) { return person.flatMap(Person::getCar) .flatMap(Car::getInsurance) .map(Insurance::getName) // 如果Optional的j結果為空值,設置默認值 .orElse("Unknown"); }
我們可以看到,處理潛在可能缺失的值時,使用Optional具有明顯的優(yōu)勢。這一次,你可以用非常容易卻又普適的方法實現(xiàn)之前你期望的效果——不再需要使用那么多的條件分支,也不會增加代碼的復雜性。
使用Optional解引用串接的Person/Car/Insurance對象
由Optional
這里,我們從以Optional封裝的Person入手,對其調(diào)用flatMap(Person::getCar)。如前所述,這種調(diào)用邏輯上可以劃分為兩步。第一步,某個Function作為參數(shù),被傳遞給由Optional封裝的Person對象,對其進行轉換。這個場景中,F(xiàn)unction的具體表現(xiàn)是一個方法引用,即對Person對象的getCar方法進行調(diào)用。由于該方法返回一個Optional
第二步與第一步大同小異,它會將Optional
截至目前為止,返回的Optional可能是兩種情況:如果調(diào)用鏈上的任何一個方法返回一個空的Optional,那么結果就為空,否則返回的值就是你期望的保險公司的名稱。那么,你如何讀出這個值呢?畢竟你最后得到的這個對象還是個Optional
我們決定采用orElse方法讀取這個變量的值,使用這種方式你還可以定義一個默認值,遭遇空的Optional變量時,默認值會作為該方法的調(diào)用返回值。Optional類提供了多種方法讀取Optional實例中的變量值。
get()是這些方法中最簡單但又最不安全的方法。如果變量存在,它直接返回封裝的變量值,否則就拋出一個NoSuchElementException異常。所以,除非你非常確定Optional變量一定包含值,否則使用這個方法是個相當糟糕的主意。此外,這種方式即便相對于嵌套式的null檢查,也并未體現(xiàn)出多大的改進。
orElse(T other)是我們在代碼使用的方法,正如之前提到的,它允許你在Optional對象不包含值時提供一個默認值。
orElseGet(Supplier extends T> other)是orElse方法的延遲調(diào)用版,Supplier方法只有在Optional對象不含值時才執(zhí)行調(diào)用。如果創(chuàng)建默認值是件耗時費力的工作,你應該考慮采用這種方式(借此提升程序的性能),或者你需要非常確定某個方法僅在Optional為空時才進行調(diào)用,也可以考慮該方式(這種情況有嚴格的限制條件)。
orElseThrow(Supplier extends X> exceptionSupplier)和get方法非常類似,它們遭遇Optional對象為空時都會拋出一個異常,但是使用orElseThrow你可以定制希望拋出的異常類型。
ifPresent(Consumer super T>)讓你能在變量值存在時執(zhí)行一個作為參數(shù)傳入的方法,否則就不進行任何操作。
Optional類和Stream接口的相似之處,遠不止map和flatMap這兩個方法。還有第三個方法filter,它的行為在兩種類型之間也極其相似。
兩個Optional 對象的組合現(xiàn)在,我們假設你有這樣一個方法,它接受一個Person和一個Car對象,并以此為條件對外部提供的服務進行查詢,通過一些復雜的業(yè)務邏輯,試圖找到滿足該組合的最便宜的保險公司:
public Insurance findCheapestInsurance(Person person, Car car) { // 不同的保險公司提供的查詢服務 // 對比所有數(shù)據(jù) return cheapestCompany; }
我們還假設你想要該方法的一個null-安全的版本,它接受兩個Optional對象作為參數(shù),返回值是一個Optional
public OptionalnullSafeFindCheapestInsurance(Optional person, Optional car) { if (person.isPresent() && car.isPresent()) { return Optional.of(findCheapestInsurance(person.get(), car.get())); } else { return Optional.empty(); } }
這個方法具有明顯的優(yōu)勢,我們從它的簽名就能非常清楚地知道無論是person還是car,它的值都有可能為空,出現(xiàn)這種情況時,方法的返回值也不會包含任何值。不幸的是,該方法的具體實現(xiàn)和你之前曾經(jīng)實現(xiàn)的null檢查太相似了:方法接受一個Person和一個Car對象作為參數(shù),而二者都有可能為null。利用Optional類提供的特性,有沒有更好或更地道的方式來實現(xiàn)這個方法呢?
Optional類和Stream接口的相似之處遠不止map和flatMap這兩個方法。還有第三個方法filter,它的行為在兩種類型之間也極其相似,我們在接下來的一節(jié)會進行介紹。
使用filter 剔除特定的值你經(jīng)常需要調(diào)用某個對象的方法,查看它的某些屬性。比如,你可能需要檢查保險公司的名稱是否為“Cambridge-Insurance”。為了以一種安全的方式進行這些操作,你首先需要確定引用指向的Insurance對象是否為null,之后再調(diào)用它的getName方法,如下所示:
Insurance insurance = ...; if(insurance != null && "CambridgeInsurance".equals(insurance.getName())){ System.out.println("ok"); }
使用Optional對象的filter方法,這段代碼可以重構如下:
OptionaloptInsurance = ...; optInsurance.filter(insurance -> "CambridgeInsurance".equals(insurance.getName())) .ifPresent(x -> System.out.println("ok"));
filter方法接受一個謂詞作為參數(shù)。如果Optional對象的值存在,并且它符合謂詞的條件,filter方法就返回其值;否則它就返回一個空的Optional對象。如果你還記得我們可以將Optional看成最多包含一個元素的Stream對象,這個方法的行為就非常清晰了。如果Optional對象為空,它不做任何操作,反之,它就對Optional對象中包含的值施加謂詞操作。如果該操作的結果為true,它不做任何改變,直接返回該Optional對象,否則就將該值過濾掉,將Optional的值置空。
下一節(jié)中,我們會探討Optional類剩下的一些特性,并提供更實際的例子,展示多種你能夠應用于代碼中更好地管理缺失值的技巧。
使用Optional 的實戰(zhàn)示例相信你已經(jīng)了解,有效地使用Optional類意味著你需要對如何處理潛在缺失值進行全面的反思。這種反思不僅僅限于你曾經(jīng)寫過的代碼,更重要的可能是,你如何與原生Java API實現(xiàn)共存共贏。
實際上,我們相信如果Optional類能夠在這些API創(chuàng)建之初就存在的話,很多API的設計編寫可能會大有不同。為了保持后向兼容性,我們很難對老的Java API進行改動,讓它們也使用Optional,但這并不表示我們什么也做不了。你可以在自己的代碼中添加一些工具方法,修復或者繞過這些問題,讓你的代碼能享受Optional帶來的威力。我們會通過幾個實際的例子講解如何達到這樣的目的。
用Optional 封裝可能為null 的值現(xiàn)存Java API幾乎都是通過返回一個null的方式來表示需要值的缺失,或者由于某些原因計算無法得到該值。比如,如果Map中不含指定的鍵對應的值,它的get方法會返回一個null。但是,正如我們之前介紹的,大多數(shù)情況下,你可能希望這些方法能返回一個Optional對象。你無法修改這些方法的簽名,但是你很容易用Optional對這些方法的返回值進行封裝。我們接著用Map做例子,假設你有一個Map
Object value = map.get("key");
使用Optional封裝map的返回值,你可以對這段代碼進行優(yōu)化。要達到這個目的有兩種方式:你可以使用笨拙的if-then-else判斷語句,毫無疑問這種方式會增加代碼的復雜度;或者你可以采用我們前文介紹的Optional.ofNullable方法:
Optional
每次你希望安全地對潛在為null的對象進行轉換,將其替換為Optional對象時,都可以考慮使用這種方法。
異常與Optional 的對比由于某種原因,函數(shù)無法返回某個值,這時除了返回null,Java API比較常見的替代做法是拋出一個異常。這種情況比較典型的例子是使用靜態(tài)方法Integer.parseInt(String),將String轉換為int。在這個例子中,如果String無法解析到對應的整型,該方法就拋出一個NumberFormatException。最后的效果是,發(fā)生String無法轉換為int時,代碼發(fā)出一個遭遇非法參數(shù)的信號,唯一的不同是,這次你需要使用try/catch 語句,而不是使用if條件判斷來控制一個變量的值是否非空。
你也可以用空的Optional對象,對遭遇無法轉換的String時返回的非法值進行建模,這時你期望parseInt的返回值是一個optional。我們無法修改最初的Java方法,但是這無礙我們進行需要的改進,你可以實現(xiàn)一個工具方法,將這部分邏輯封裝于其中,最終返回一個我們希望的Optional對象,代碼如下所示。
public static OptionalstringToInt(String s) { try { return Optional.of(Integer.parseInt(s)); } catch (NumberFormatException e) { return Optional.empty(); } }
我們的建議是,你可以將多個類似的方法封裝到一個工具類中,讓我們稱之為OptionalUtility。通過這種OptionalUtility.stringToInt方法,將String轉換為一個Optional
Github: chap10
Gitee: chap10
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/72006.html
摘要:但返回的是一個類型的對象,這意味著操作的結果是一個類型的對象。反之,如果對象存在,這次調(diào)用就會將其作為函數(shù)的輸入,并按照與方法的約定返回一個對象。 一、Optional 類入門 Java 8中引入了一個新的類java.util.Optional。變量存在時,Optional類只是對類簡單封裝。變量不存在時,缺失的值會被建模成一個空的Optional對象,由方法Optional.empt...
摘要:跳過元素流還支持方法,返回一個扔掉了前個元素的流。歸約到目前為止,我們見到過的終端操作都是返回一個之類的或?qū)ο蟮取_@樣的查詢可以被歸類為歸約操作將流歸約成一個值。通過反復使用加法,你把一個數(shù)字列表歸約成了一個數(shù)字。 使用流 在上一篇的讀書筆記中,我們已經(jīng)看到了流讓你從外部迭代轉向內(nèi)部迭代。這樣,你就用不著寫下面這樣的代碼來顯式地管理數(shù)據(jù)集合的迭代(外部迭代)了: /** * 菜單 ...
摘要:收集器用作高級歸約剛剛的結論又引出了優(yōu)秀的函數(shù)式設計的另一個好處更易復合和重用。更具體地說,對流調(diào)用方法將對流中的元素觸發(fā)一個歸約操作由來參數(shù)化。另一個常見的返回單個值的歸約操作是對流中對象的一個數(shù)值字段求和。 用流收集數(shù)據(jù) 我們在前一章中學到,流可以用類似于數(shù)據(jù)庫的操作幫助你處理集合。你可以把Java 8的流看作花哨又懶惰的數(shù)據(jù)集迭代器。它們支持兩種類型的操作:中間操作(如 filt...
摘要:第三個問題查找所有來自于劍橋的交易員,并按姓名排序。第六個問題打印生活在劍橋的交易員的所有交易額。第八個問題找到交易額最小的交易。 付諸實戰(zhàn) 在本節(jié)中,我們會將迄今學到的關于流的知識付諸實踐。我們來看一個不同的領域:執(zhí)行交易的交易員。你的經(jīng)理讓你為八個查詢找到答案。 找出2011年發(fā)生的所有交易,并按交易額排序(從低到高)。 交易員都在哪些不同的城市工作過? 查找所有來自于劍橋的交易...
摘要:首先我們定義一個有兩個不同控制器的然后,我們創(chuàng)建一個特定的工廠接口來創(chuàng)建新的對象不需要手動的去繼承實現(xiàn)該工廠接口,我們只需要將控制器的引用傳遞給該接口對象就好了的控制器會自動選擇合適的構造器方法。這種指向時間軸的對象即是類。 本文為翻譯文章,原文地址 這里 歡迎來到本人對于Java 8的系列介紹教程,本教程會引導你一步步領略最新的語法特性。通過一些簡單的代碼示例你即可以學到默認的接口方...
閱讀 652·2021-11-23 09:51
閱讀 3599·2021-11-15 11:38
閱讀 926·2021-10-14 09:42
閱讀 3162·2021-09-29 09:35
閱讀 2104·2021-09-03 10:33
閱讀 769·2021-07-30 16:33
閱讀 1558·2019-08-30 15:55
閱讀 1840·2019-08-30 14:04