摘要:常量接口是對接口的一種不良使用。如果這些常量最好被看作是枚舉類型成員,那就應該用枚舉類型來導出。因為客戶端既不能創建枚舉類型的實例,也不能對它進行擴展,因此很可能沒有實例,而只有聲明過的枚舉常量。換句話說,枚舉類型是實例受控的。
問題
我們偶爾能在項目中看到如下風格的代碼:
public class ResponseCode { public static final int SUCCESS = 0; public static final int FAILURE = 10000; public static final int ILLEGAL_ARGUMENT = 10001; //........ }
這樣的代碼一般被叫做int枚舉模式。其包含著大量缺點:
int枚舉是編譯時常量,被編譯到客戶端中,如果枚舉常量關聯的int發生變化,客戶端必須重新編譯,如果沒有重新編譯,程序仍可以運行,但行為就不確定了。
將int枚舉常量翻譯成可打印的字符串很麻煩。
在本例中,我要向客戶端返回一個錯誤碼和錯誤信息,錯誤碼可以通過ResponseCode.FAILURE獲得,但是錯誤信息恐怕只能以"FAILURE"這樣的hard code返回了。
遍歷一個組中所有的int枚舉常量,獲得int枚舉組的大小,沒有可靠的方法。
在這種int枚舉模式的基礎上,還有一種變體,我們稱之為String枚舉模式。雖然它提供了可打印的字符串,但這種方式存在性能問題,因為依賴于字符串的比較操作。
還有一種代碼類似:
public interface CascadeConstant { String DELETION_CHECK_CODE = "deletion.check"; String DELETION_DELETE_CODE = "deletion.delete"; String DELETION_FORCE_DELETE_CODE = "deletion.forceDelete"; String DELETION_CLEANUP_CODE = "deletion.cleanup"; ListDELETION_CODES = Arrays.asList(DELETION_CHECK_CODE, DELETION_DELETE_CODE, DELETION_FORCE_DELETE_CODE); }
這樣的代碼我們一般稱之為常量接口(constant interface)——這種接口不包含任何方法,它只包含靜態的final域,每個域都導出一個常量。
類實現接口時,接口就充當可以引用這個類的實例類型。因此,類實現了接口,就表明客戶端對這個類的實例可以實施某些動作。為了任何其他目的而定義的接口是不恰當的。
?常量接口是對接口的一種不良使用。類在內部使用某些常量,純粹是實現細節,實現常量接口,會導致把這樣的實現細節泄露到該類的導出API中,因為接口中所有的域都是及方法public的。類實現常量接口,這對于這個類的用戶來講并沒有實際的價值。實際上,這樣做返回會讓他們感到更糊涂,這還代表了一種承諾:如果在將來的發行版本中,這個類被修改了,它不再需要使用這些常量了,依然必須實現這個接口,以確保二進制兼容性。如果非final類實現了常量接口,它的所有子類的命名空間都受到了污染。Java平臺類庫中存在幾個常量接口,如java.io.ObjectStreamConstants,這些接口都是反面典型,不值得效仿。
那既然不適合存在全部都是導出常量的常量接口,那么如果需要導出常量,它們應該放在哪里呢?如果這些常量與某些現有的類或者接口緊密相關,就應該把這些常量添加到這個類或者接口中,注意,這里說添加到接口中并不是指的常量接口。在Java平臺類庫中所有的數值包裝類都導出MIN_VALUE和MAX_VALUE常量。如果這些常量最好被看作是枚舉類型成員,那就應該用枚舉類型來導出。否則,應該使用不可實例化的工具類來導出這些常量。
實踐我們先看改良版的ResponseCode :
public enum ResponseCode { SUCCESS(0), ERROR(10000), ILLEGAL_ARGUMENT(10001); private final int code; ResponseCode(int code) { this.code = code; } public int getCode() { return code; } public static ResponseCode getEnum(int value) { for (ResponseCode responseCode : ResponseCode.values()) { if (responseCode.getCode()==value) { return responseCode; } } return null; } }
這樣就克服了我們之前提到的缺點:類型確定。
在int枚舉模式中或者String枚舉模式中,我們會寫出這樣的方法簽名:
//int 枚舉模式 SendResponse(String description,int value) //String枚舉模式 //SendResponse(String description,int value)
這樣的代碼其實并不可靠,我們可以考慮:
SendResponse(String description,int code)
那么在調用的時候實質上是:
SendResponse(ResponseCode.ERROR.toString(),ResponseCode.ERROR.getCode())
為了更加方便,我們可以在此之上簡單的封裝一層:
AutoBuildAndSendResponse(ResponseCode responseCode){ SendResponse(responseCode.toString(),responseCode.getCode(); }
這就很美滋滋。
在這個情況下,我們甚至還可以考慮在SendResponse方法中再加一個名為errorDetail的參數。利用方法重載,使Reponse的返回信息更為靈活。
不僅如此,Java的枚舉類是很強大的。其本質是int值,并且背后基本原理也非常簡單:它們就是通過共有的靜態final域為每個枚舉常量導出實例的類。因為沒有可以訪問的構造器,枚舉類是真正的final。因為客戶端既不能創建枚舉類型的實例,也不能對它進行擴展,因此很可能沒有實例,而只有聲明過的枚舉常量。換句話說,枚舉類型是實例受控的。而且,Java的枚舉類還可以添加任意的方法和域,并實現任意接口,而且也提供了所有的Object方法的高級實現。
關于StringValue的較佳實踐public enum SourceDiskType { SYSTEM("system"), DATA("data"),; private String stringValue; SourceDiskType(String stringValue) { setStringValue(stringValue); } public String getStringValue() { return stringValue; } public void setStringValue(String stringValue) { this.stringValue = stringValue; } public static SourceDiskType getEnum(String stringValue) { if (null == stringValue) { return null; } for (SourceDiskType sourceDiskType : SourceDiskType.values()) { if (sourceDiskType.getStringValue().equals(stringValue)) { return sourceDiskType; } } return null; } }
這是阿里云早期版本SDK中的一段代碼。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/70211.html
摘要:而用關鍵字調用構造器,總是會創建一個新的對象,無論內容是否相同。中對象的哈希碼被頻繁地使用比如在等容器中。字符串不變性保證了碼的唯一性因此可以放心地進行緩存。對于所有包含方式新建對象包括的連接表達式,它所產生的新對象都不會被加入字符串池中。 前言 前陣子和同事在吃飯時聊起Java的String,覺得自己之前的筆記寫的略顯零散。故此又重新整理了一下。 String在Java中算是一個有意...
摘要:一前言本文章將以報表下載為例,給大家介紹三種文件下載的方式。通過二進制數據流的方式下載這種方式是我目前采用的方式,用于處理報表下載。缺點對于數據量不大的文件,這種方式是可行的。 一、前言 本文章將以excel報表下載為例,給大家介紹三種文件下載的方式。 原文地址:簡談文件下載的三種方式 | Rychou 二、正文 1. 通過服務器文件地址下載 這是最常見的文件下載方式,大多數網站的音頻...
摘要:一前言本文章將以報表下載為例,給大家介紹三種文件下載的方式。通過二進制數據流的方式下載這種方式是我目前采用的方式,用于處理報表下載。缺點對于數據量不大的文件,這種方式是可行的。 一、前言 本文章將以excel報表下載為例,給大家介紹三種文件下載的方式。 原文地址:簡談文件下載的三種方式 | Rychou 二、正文 1. 通過服務器文件地址下載 這是最常見的文件下載方式,大多數網站的音頻...
摘要:所以經常看到的說閉包就是綁定了上下文環境的函數。我更偏向于閉包是一個函數和聲明該函數的詞法環境的組合。里面的閉包先上一個閉包該例子的解釋上面的代碼,在函數里面定義的函數和這個函數聲明的詞法環境就形成了一個閉包。 閉包是什么 第一種說法:閉包創建一個詞法作用域,這個作用域里面的變量被引用之后可以在這個詞法作用域外面被自由訪問,是一個函數和聲明該函數的詞法環境的組合 第二種說法:閉包就是...
閱讀 1876·2021-09-28 09:36
閱讀 2426·2021-09-08 09:35
閱讀 3067·2019-08-30 15:53
閱讀 1554·2019-08-30 14:08
閱讀 665·2019-08-29 18:40
閱讀 2843·2019-08-29 13:57
閱讀 2702·2019-08-29 13:55
閱讀 681·2019-08-26 13:45