Java和Ceylon對(duì)象的構(gòu)造和驗(yàn)證
當(dāng)變換Java代碼為Ceylon代碼時(shí),有時(shí)候我會(huì)遇到一些Java類構(gòu)造器混淆了驗(yàn)證與初始化的情形。讓我們使用一個(gè)簡(jiǎn)單但是人為的代碼例子來說明我想闡述的意思。
一些壞代碼
考慮下面的Java類。(伙計(jì),不要在家里寫這樣的代碼)
public class Period {
private final Date startDate;
private final Date endDate;
//returns null if the given String
//does not represent a valid Date
private Date parseDate(String date) {
...
}
public Period(String start, String end) {
startDate = parseDate(start);
endDate = parseDate(end);
}
public boolean isValid() {
return startDate!=null && endDate!=null;
}
public Date getStartDate() {
if (startDate==null)
throw new IllegalStateException();
return startDate;
}
public Date getEndDate() {
if (endDate==null)
throw new IllegalStateException();
return endDate;
}
}
嘿,我之前已經(jīng)警告過,它是人為的。但是,在實(shí)際Java代碼中找個(gè)像這樣的東西實(shí)際上并非不常見。
這里的問題在于,即使輸入?yún)?shù)(在隱藏的parseDate()方法中)的驗(yàn)證失敗了,我們還是會(huì)獲得一個(gè)Period的實(shí)例。但是我們獲取的那個(gè)Period不是一個(gè)“有效的”狀態(tài)。嚴(yán)格地說,我的意思是什么呢?
好吧,假如一個(gè)對(duì)象不能有意義地響應(yīng)公用操作時(shí),我會(huì)說它處于一個(gè)非有效狀態(tài)。在這個(gè)例子里,getStartDate() 和getEndDate()會(huì)拋出一個(gè)IllegalStateException異常,這就是我認(rèn)為不是“有意義的”一種情況。
從另外一方面來看這個(gè)例子,在設(shè)計(jì)Period時(shí),我們這兒出現(xiàn)了類型安全的失敗。未檢查的異常代表了類型系統(tǒng)中的一個(gè)“漏洞”。因此,一個(gè)更好的Period的類型安全的設(shè)計(jì),會(huì)是一個(gè)不使用未檢查的異常—在這個(gè)例子中意味著不拋出IllegalStateException異常。
(實(shí)際上,在真實(shí)代碼中,我更有可能遇到一個(gè)getStartDate() 方法它不檢查null ,在這個(gè)代碼行之后就會(huì)導(dǎo)致一個(gè)NullPointerException異常,這就更加糟糕了。)
我們能夠很容易地轉(zhuǎn)換上面的Period類成為Ceylon形式的類:
shared class Period(String start, String end) {
//returns null if the given String
//does not represent a valid Date
Date? parseDate(String date) => ... ;
value maybeStartDate = parseDate(start);
value maybeEndDate = parseDate(end);
shared Boolean valid
=> maybeStartDate exists
&& maybeEndDate exists;
shared Date startDate {
assert (exists maybeStartDate);
return maybeStartDate;
}
shared Date endDate {
assert (exists maybeEndDate);
return maybeEndDate;
}
}
當(dāng)然了,這段代碼也會(huì)遇到與原始Java代碼同樣的問題。兩個(gè)assert符號(hào)沖著我們大喊,在代碼的類型安全中有一個(gè)問題。
使Java代碼變得更好
Java里我們?cè)趺锤倪M(jìn)這段代碼呢?好吧,這兒就是一個(gè)例子關(guān)于Java飽受詬病的已檢查異常會(huì)是一個(gè)非常合理的解決方法!我們可以稍微修改下Period來從它的構(gòu)造器中拋出一個(gè)已檢查的異常:
public class Period {
private final Date startDate;
private final Date endDate;
//throws if the given String
//does not represent a valid Date
private Date parseDate(String date)
throws DateFormatException {
...
}
public Period(String start, String end)
throws DateFormatException {
startDate = parseDate(start);
endDate = parseDate(end);
}
public Date getStartDate() {
return startDate;
}
public Date getEndDate() {
return endDate;
}
}
現(xiàn)在,使用這個(gè)解決方案,我們就不會(huì)獲取一個(gè)處于非有效狀態(tài)的Period,實(shí)例化Period的代碼會(huì)由編譯器負(fù)責(zé)去處理無效輸入的情形,它會(huì)捕獲一個(gè)DateFormatException異常。
try {
Period p = new Period(start, end);
...
}
catch (DateFormatException dfe) {
...
}
這是一個(gè)對(duì)已檢查異常不錯(cuò)的、完美的、正確的使用,不幸的是我?guī)缀鹾苌倏吹絁ava代碼像上面這樣使用已檢查異常。
使Ceylon代碼變得更好
那么Ceylon怎么樣呢?Ceylon沒有已檢查異常,因而我們需要尋找一個(gè)不同的解決方式。典型地,在Java調(diào)用一個(gè)函數(shù)會(huì)拋出一個(gè)已檢查異常的情形中,Ceylon會(huì)調(diào)用函數(shù)返回一個(gè)聯(lián)合類型。因?yàn)椋粋€(gè)類的初始化不返回除了類自己外的任何類型,我們需要提取一些混合的初始化/驗(yàn)證的邏輯來使其成為一個(gè)工廠函數(shù)。
//returns DateFormatError if the given
//String does not represent a valid Date
Date|DateFormatError parseDate(String date) => ... ;
shared Period|DateFormatError parsePeriod
(String start, String end) {
value startDate = parseDate(start);
if (is DateFormatError startDate) {
return startDate;
}
value endDate = parseDate(end);
if (is DateFormatError endDate) {
return endDate;
}
return Period(startDate, endDate);
}
shared class Period(startDate, endDate) {
shared Date startDate;
shared Date endDate;
}
根據(jù)類型系統(tǒng),調(diào)用者有義務(wù)去處理DateFormatError:
value p = parsePeriod(start, end);
if (is DateFormatError p) {
...
}
else {
...
}
或者,如果我們不關(guān)心給定日期格式的實(shí)際問題(這是有可能的,假定我們工作的初始化代碼丟失了那個(gè)信息),我們可以使用Null而不是DateFormatError:
//returns null if the given String
//does not represent a valid Date
Date? parseDate(String date) => ... ;
shared Period? parsePeriod(String start, String end)
=> if (exists startDate = parseDate(start),
exists endDate = parseDate(end))
then Period(startDate, endDate)
else null;
shared class Period(startDate, endDate) {
shared Date startDate;
shared Date endDate;
}
至少可以說,使用工廠函數(shù)的方法是優(yōu)秀的,因?yàn)橥ǔ碚f在驗(yàn)證邏輯和對(duì)象初始化之間它具有更好的隔離。這點(diǎn)在Ceylon中特別有用,在Ceylon中,編譯器在對(duì)象初始化邏輯中添加了一些非常嚴(yán)厲的限制,以保證對(duì)象的所有領(lǐng)域僅被賦值一次。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- java構(gòu)造函數(shù)示例(構(gòu)造方法)
- Java中子類調(diào)用父類構(gòu)造方法的問題分析
- Java中構(gòu)造、生成XML簡(jiǎn)明教程
- java繼承中的構(gòu)造方法實(shí)例解析
- 簡(jiǎn)單談?wù)刯ava中匿名內(nèi)部類構(gòu)造函數(shù)
- Java基礎(chǔ)教程之構(gòu)造器與方法重載
- java用靜態(tài)工廠代替構(gòu)造函數(shù)使用方法和優(yōu)缺點(diǎn)
- 使用Java構(gòu)造和解析Json數(shù)據(jù)的兩種方法(詳解一)
- 講解Java中如何構(gòu)造內(nèi)部類對(duì)象以及訪問對(duì)象
- java中的靜態(tài)代碼塊、構(gòu)造代碼塊、構(gòu)造方法詳解
相關(guān)文章
springboot程序啟動(dòng)慢-未配置hostname的解決
這篇文章主要介紹了springboot程序啟動(dòng)慢-未配置hostname的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
關(guān)于IntelliJ IDEA 打包代碼報(bào)錯(cuò)的問題
這篇文章主要介紹了關(guān)于IntelliJ IDEA 打包代碼報(bào)錯(cuò)的問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
Spring容器注冊(cè)組件實(shí)現(xiàn)過程解析
這篇文章主要介紹了Spring容器注冊(cè)組件實(shí)現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03
Java中的數(shù)組基礎(chǔ)知識(shí)學(xué)習(xí)教程
這篇文章主要介紹了Java中的數(shù)組基礎(chǔ)知識(shí)學(xué)習(xí)教程,文中同時(shí)也整理了Java對(duì)數(shù)字類型的支持狀況及Number類中的方法,需要的朋友可以參考下2016-02-02
Java中Quartz高可用定時(shí)任務(wù)快速入門
如果你想做定時(shí)任務(wù),有高可用方面的需求,或者僅僅想入門快,上手簡(jiǎn)單,那么選用它準(zhǔn)沒錯(cuò),感興趣的小伙伴們可以參考一下2022-04-04
Java中spring boot validation自定義注解使用方式
這篇文章主要介紹了Java中spring boot validation自定義注解使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08

