Java中對於多個返回參數的選項是有限制的一種方法只能返回一個對象數組或原始函數和其他語言不同的是它不會提供一種簡易方式來消耗方法調用中的參數實際上我們的選擇是返回一個對象數組一個集合僅為返回的參數創建一個類或者最終將其發送到你打算替換的對象中所有這些方法都存在缺陷
使用對象數組
如果我們能夠幸運地獲取一套同類的返回參數那麼對象的數組就會是一個帶有例外的選項當我們打開這些對象時需要能分辨出每個參數從另一方面來說如果我們正在返回多個參數的不同類型我們需要使用所有超類對象的數組最有可能的就是對象本身然後我們要拋出每一個打開的參數我們已經丟失了類型安全性且返回參數命令出錯的機會也可能增加
使用集合
與使用數組的方法類似我們或許也能創造一個集合來實現返回在集合之上使用數組主要是因為創建集合所需要代碼的數量要比這段代碼短
List< Object> retVal = new ArrayList< Object>();
retValadd(string);
retValadd(num);
retValadd(object);
return retVal;
而創建集合的代碼要高於使用數組初始化設置
return new Object[] {string num object}
事實上在數組之上使用集合沒有什麼真正的優勢除非我們要使用映射以便通過名稱或其他要素來返回值
首次創建Java時其簡單程度是對日趨復雜的c++的一種顛覆指針和內存管理被簡化了包括去除參數間接常量函數指針以及其他功能強大但容易混淆的性能在c++中我們可以用值或參照傳遞參數這樣可以對方法中的參照進行重新分配並為你提供參數或返回更多值的方式
使用JavaBeans
C++也支持的structs允許簡單結構化的數據包當然Java類可以簡單完成雙倍於structs的任務但通常習慣以大量模板來擴大源碼
使用類和JavaBeans慣例的時候還存在一個問題即對象本質上是易變的這些對象有可能被方法的調用者和方法調用的類共享這樣就會導致易變狀態的共享而這中情況無益於多線程系統
在Java中你能用值傳遞的只剩下方法參數了而且還不能是outparams同時方法只能返回一個參數看一下任意代碼庫就會發現大量的實例不過卻不是十分有效
改進Java Beans方式
那麼應該怎樣做呢?Java類選項事實上才是解決方案的關鍵以及對其方式進行改進這些類可以成為structs更好的替代物
讓我們為返回的類確定兩個參數名稱和出生日期
public class PersonNameDOB {
private String name;
private Date dob;
public Date getDob() {
return dob;
}
public void setDob(Date dob) {
thisdob = dob;
}
public String getName() {
return name;
}
public void setName(String name) {
thisname = name;
}
}
顯然這是一個人為的例子我們有機會擁有一個已經定義的Person類大家肯定也會有類似的例子需要從方法中返回兩個不同的對象但是卻沒有已經為其定義的類或者是返回的類夾帶多余的信息或許是比這更糟的情況例如如果有人調用了你的方法來使用或修改返回對象中的值
上述情況所需代碼更多因此我們可以做一些簡單的修改
public class PersonNameDOB {
public final String name;
public final Date dob;
public PersonNameDOB(String name Date dob) {
thisname = name;
thisdob = dob;
}
}
其結果要短一些且更適合這項任務值已經返回了因此不需要setters了我們只要在返回對象建成後創建值就可以了它們不需要更改由於它們位於構造器中因此具有決定性作用現在任務已經完成將類的屬性設為公開也沒有風險了同理可以處理getters了其結果更短更易於使用
PersonNameDOB personNameDOB = SSNLookuplookupBySSN();
Systemoutprintln(personNameDOBname);
Systemoutprintln(personNameDOBdob);
lookupBySSN方法
public PersonNameDOB lookupBySSN(String ssn) {
Find the person record in the DB etc
return new PersonNameDOB(persongetName() persongetDOB());
}
如果這些太顯而易見請耐心看下去我喜歡用這個方法來簡化對象的返回這種類型是安全的因此在返回後不需要將對象拋出數組而最後屬性的修改意味著這些返回的對象不會被濫用它們只是為了傳輸數據
為了安全起見建議你復制對象或使用不可改變的對象以應付值的意外修改在我們的例子中字符串是不可改變的但是日期可以復制
public PersonNameDOB lookupBySSN(String ssn) {
Find the person record in the DB etc
return new PersonNameDOB(persongetName() new Date(persongetDOB()getTime()));
}
這可以阻止調用者做接下來的操作
PersonNameDOB personNameDOB = SSNLookuplookupBySSN();
personNameDOBdobsetTime();
成對的需求
以上的模式是筆者經常在Java應用程序接口調用中用來替代structs的方法但是如果我們只是想返回兩個類型對象這些就還不夠看上去是唾手可得的東西其實仍然從JavaSE標准分配中神秘失蹤而這就是被原始化的Pair類看看我們如何從上述模式來建立Pair類
首先值要比名稱和出生日期都普遍最普遍的是在將域名定為first和second
public class Pair {
public final String first;
public final Date second;
public Pair(String first Date second) {
thisfirst = first;
thissecond = second;
}
}
現在擁有了一個可以返回Strings和Dates對的通用類但還不包括其他類型將其擴展為通用類型
public class Pair< A B> {
public final A first;
public final B second;
public Pair(A first B second) {
thisfirst = first;
thissecond = second;
}
}
這樣就好多了沒必要擔心代表了一對返回類型快捷方式的通配符這個類現在可以作為通用類型對來使用如
public static Pair< String Date> lookupBySSN(String ssn) {
// find the person in the DB
return new Pair(persongetName() new Date(persongetDOB()getTime()));
}
開始使用
Pair< String Date> personNameDOB = SSNLookuplookupBySSN();
Systemoutprintln(personNameDOBfirst);
Systemoutprintln(personNameDOBsecond);
Pair類還未完成
我們還要考慮類型是否真具有普遍性
我們不想其他人擴展或更改Pair類因為這可能破壞類的最初意圖
新的new Pair()是可以的但是看上去有些不雅我們可以改進一下
Pair對於返回值是很有效的但是如果要作為映射中的關鍵要素就有些勉為其難
擁有用於調試或其他toString()用途的Pair字符串代表形式是非常好的
最後允許Pair和包含的對象可被序列化其內容也被假定為可以序列化了
因此讓我們看看它是如何改變Pair執行的
public final class Pair< AB> implements Serializable {
private static final long serialVersionUID = L; // shouldnt
// need to change
public final A first;
public final B second;
private Pair (A first B second) {
thisfirst = first;
thissecond = second;
}
public static < AB> Pair< AB> of (A first B second) {
return new Pair< AB>(firstsecond);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != objgetClass()) {
return false;
}
final Pair other = (Pair) obj;
if (thisfirst != otherfirst &&
(thisfirst == null || !thisfirstequals(otherfirst))) {
return false;
}
if (thissecond != othersecond &&
(thissecond == null || !thissecondequals(othersecond))) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = ;
hash = * hash + (thisfirst != null ?
thisfirsthashCode() : );
hash = * hash + (thissecond != null ? thissecondhashCode() : );
return hash;
}
@Override
public String toString () {
return Stringformat(Pair[%s%s] firstsecond);
}
}
這樣就已經將構造器私有化
但是提供了一個看起來更適合的靜態of()方法
return Pairof(persongetName() new Date(persongetDOB()getTime()));
Pair類已經完成因此沒有人可以取代它或改變類的原始意圖它看上去很嚴格但卻可以用於廣泛的使用如果有人想讓Pair以不同方式運作就應該寫出適合自己的執行代碼並對其進行求證
equals()和hash()方法意味著這個類不止可以用來返回值它們還可以成為映射的關鍵此處對使用該模式返回對象給出的建議是讓IDE為我們創建equals和hashCode方法IDE往往都有可插入的模板其中包含已定義的最佳方法因此我們只需讓NetBeans創建就可以了
toString()給出了pair的合適形式如 Pair[FredJonesSun Mar :: PDT ]這對於調試彈出很有用
這一個類現在執行Serializable相信此處我們的選擇令人懷疑但是集合是可序列化的Pair應該也可以如果Pair中的類不能序列化那麼就如果集合中的類不能序列化一樣糟糕而Pair不應該阻止類中間的序列化
完成了嗎?
既是又不是這個類現在在Navigenics中使用毫無疑問在其他Java應用程序中也存在類似的執行那我們完成了沒有呢?從最具預見性的角度來講我們完成了從任何時候都需要的角度來說恐怕還沒完成
例如這類Pairs不能做比較增加一個compareTo()方法使之可比但是要注意應對復雜的通用類設計通常比較第一個值很容易如果它們相等就要比較第二個值這可能是最合適的行為但是Pair的每次使用都正確嗎?我們要檢查一下作比較的類是否具有可比性如果不具備該怎麼辦?
結論
為Java創建一個Pair類是有些難的而且在簡單通用的類庫執行中類庫要求在少量功能前提下不改變Java語言通用性是很麻煩的一件事不過本人相信本文描述的Pair類至少是SE庫中標准化Pair執行的良好開端但願在Java 中會增加這一項盡管它對於其他語言中的Tuple來說是顯得有些貧乏但是加入標准的Pair有望為我們帶來大量實用性這種可讀性的改進以及模板代碼的刪除都旨在減少Coin項目的語言變化
From:http://tw.wingwit.com/Article/program/Java/hx/201311/26974.html