Android基礎(chǔ)進(jìn)階之EffectiveJava翻譯系列(第六章:方法)

這一章介紹方法設(shè)計(jì)的幾個(gè)方面:如何對(duì)待參數(shù)和返回值,如何設(shè)計(jì)方法簽名,如何注釋方法

Item38: 檢查參數(shù)的合法性

大部分使用的方法參數(shù)都有一定的限制,如不為null,size>0等
通用的原則就是預(yù)防大于整改,提前發(fā)現(xiàn)錯(cuò)誤可以更快的規(guī)避問(wèn)題,而不是在程序運(yùn)行中發(fā)生

對(duì)于公共方法高蜂,使用Javadoc@塊標(biāo)記,來(lái)記錄在違反參數(shù)值限制時(shí)拋出的異常(Item62)舵盈。

/**
* Returns a BigInteger whose value is (this mod m). This method
* differs from the remainder method in that it always returns a
* non-negative BigInteger.
*
* @param m the modulus, which must be positive
* @return this mod m
* @throws ArithmeticException if m is less than or equal to 0
*/
public BigInteger mod(BigInteger m) {
    if (m.signum() <= 0)
        throw new ArithmeticException("Modulus <= 0: " + m);
    ... // Do the computation
}

對(duì)于私有的方法則使用斷言

// Private helper function for a recursive sort
private static void sort(long a[], int offset, int length) {
    assert a != null;
    assert offset >= 0 && offset <= a.length;
    assert length >= 0 && length <= a.length - offset;
    ... // Do the computation
}

assert在實(shí)際項(xiàng)目中使用的很少,更多的還是使用的if判斷

每次寫(xiě)一個(gè)方法或者構(gòu)造函數(shù)的時(shí)候,在方法的開(kāi)頭考慮參數(shù)的合法性是必不可少的

Item39: 必要的時(shí)候使用拷貝對(duì)象

Java是一門(mén)安全的語(yǔ)言,即使在Java語(yǔ)言中,也要假設(shè)用戶正想方設(shè)法的破壞你的程序.除了少部分人想破壞系統(tǒng)的安全性,大部分問(wèn)題都是編程人員可以控制的
雖然沒(méi)有對(duì)象的幫助,另一個(gè)類(lèi)不太可能改變對(duì)象的內(nèi)部狀態(tài),但偶爾也有疏忽的地方

//一個(gè)不可變的周期類(lèi)
// Broken "immutable" time period class
public final class Period {
    private final Date start;
    private final Date end;
    /**
    * @param start the beginning of the period
    * @param end the end of the period; must not precede start
    * @throws IllegalArgumentException if start is after end
    * @throws NullPointerException if start or end is null
    */
    public Period(Date start, Date end) {
        if (start.compareTo(end) > 0)
            throw new IllegalArgumentException(
                start + " after " + end);
        this.start = start;
        this.end = end;
    }
    public Date start() {
        return start;
    }

    public Date end() {
        return end;
    }
    ... // Remainder omitted
}

其中Date對(duì)象是可變的

// bad
Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78); // 修改了p的內(nèi)部狀態(tài)

為了保護(hù)對(duì)象的狀態(tài),我們需要在構(gòu)造函數(shù)中對(duì)可變參數(shù)執(zhí)行拷貝防御

// Repaired constructor - makes defensive copies of parameters
public Period(Date start, Date end) {
    this.start = new Date(start.getTime());
    this.end = new Date(end.getTime());
    if (this.start.compareTo(this.end) > 0)
        throw new IllegalArgumentException(start +" after "+ end);
}

// Repaired accessors - make defensive copies of internal fields
public Date start() {
    return new Date(start.getTime());
}
public Date end() {
    return new Date(end.getTime());
}

這里沒(méi)有使用clone方法,因?yàn)镈ate有其它不可信任的子類(lèi)

經(jīng)驗(yàn)上講,在內(nèi)部盡量不要使用可變的類(lèi),如Date,可用long型替代Date.getTime()

總結(jié):如果一個(gè)類(lèi)調(diào)用了或返回可變的對(duì)象,則需要用拷貝對(duì)象防御,如果很信任調(diào)用者不會(huì)修改類(lèi)的內(nèi)部狀態(tài),則需要有一份警告文檔提示調(diào)用者不能修改類(lèi)的狀態(tài)

Item 40: 如何設(shè)計(jì)方法
  • 選擇一個(gè)合適的方法名稱(chēng)
    你的主要目標(biāo)是設(shè)計(jì)一個(gè)利于理解的方法名,次要目標(biāo)是方法名稱(chēng)之間保持協(xié)調(diào)性,如向數(shù)據(jù)庫(kù)中插入一條數(shù)據(jù),有的使用addXX,有的使用setXX,有的使用insertXX,盡量保持統(tǒng)一
  • 不要過(guò)分的使用方法
    太多的方法容易使一個(gè)類(lèi)難于維護(hù)和測(cè)試,只要當(dāng)它需要經(jīng)常調(diào)用的時(shí)候才考慮提出一個(gè)方法,否則就不管它
  • 避免參數(shù)長(zhǎng)的方法
    盡量保持在四個(gè)參數(shù)或以下
    有三種方式避免長(zhǎng)參數(shù)
    1.提出更多的方法
    2.使用輔助類(lèi)保存這些參數(shù)
    3.使用建造者模式(Builder)
  • 對(duì)于傳入的參數(shù),有接口可以傳就使用接口
    如需要傳入HashMap 則在方法中將參數(shù)類(lèi)型改為Map 避免使用者只能使用HashMap,也可以傳入其它Map接口的子類(lèi)型
  • 對(duì)于布爾型參數(shù),使用枚舉更合適
    例如晰绎,您可能有一個(gè)帶有靜態(tài)工廠的溫度計(jì)類(lèi)型( Thermometer 類(lèi))山宾,其值為枚舉:
public enum TemperatureScale { FAHRENHEIT, CELSIUS }

Thermometer.newInstance(TemperatureScale.CELSIUS)不僅比Thermometer.newInstance(true)更有意義,而且可以在將來(lái)的發(fā)行版中將Kelvin添加到TemperatureScale,而不需要 在Thermometer 類(lèi)中增加一個(gè)新的靜態(tài)工廠方法

Item41: 謹(jǐn)慎地使用重載

看如下的例子,我們想?yún)^(qū)分放進(jìn)去的是List或者set或者不知道什么類(lèi)型的集合,想一下它會(huì)如何打印

public class CollectionClassifier {
public static String classify(Set<?> s) {
    return "Set";
}
public static String classify(List<?> lst) {
    return "List";
}
public static String classify(Collection<?> c) {
    return "Unknown Collection";
}
public static void main(String[] args) {
    Collection<?>[] collections = {
        new HashSet<String>(),
        new ArrayList<BigInteger>(),
        new HashMap<String, String>().values()
    };
    for (Collection<?> c : collections)
    System.out.println(classify(c));
 }
}

它會(huì)順序打印"Set","List","Unknown Collection"嗎?不,它會(huì)打印三次"Unknown Collection" 原因在于重載方法是在編譯時(shí)執(zhí)行,所以會(huì)以Collection<?>為準(zhǔn)

我們應(yīng)該避免使用相同參數(shù)數(shù)量的重載方法,使機(jī)器不懂,自己更易混淆

Item 42: 謹(jǐn)慎的使用可變參數(shù)

舉一個(gè)例子

static int sum(int... args) {
    int sum = 0;
    for (int arg : args)
        sum += arg;
    return sum;
}

sum(1,2,3) --> 6
sum() --> 0

暫略

Item 43: Return empty arrays or collections, not nulls

像如下的代碼很常見(jiàn)

//bad
private final List<Cheese> cheesesInStock = ...;
/**
 * @return an array containing all of the cheeses in the shop,
 * or null if no cheeses are available for purchase.
 */
public Cheese[] getCheeses() {
    if (cheesesInStock.size() == 0)
        return null;
    ...
}   

調(diào)用者很可能粗心大意忘記判null導(dǎo)致異常,也許很多年之后才會(huì)發(fā)現(xiàn)
有人說(shuō)返回null避免了內(nèi)存開(kāi)銷(xiāo),首先你要證明是這段代碼導(dǎo)致的性能問(wèn)題,其次我們可以使用不可變的靜態(tài)常量聲明一個(gè)空集合

// The right way to return an array from a collection
private final List<Cheese> cheesesInStock = ...;
private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0];
/**
* @return an array containing all of the cheeses in the shop.
*/
public Cheese[] getCheeses() {
    if(cheesesInStock.size() <= 0)
        return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY);
}

//or

public List<Cheese> getCheeseList() {
 if (cheesesInStock.isEmpty())
    return Collections.emptyList(); // Always returns same list
 else
    return new ArrayList<Cheese>(cheesesInStock);
}

總之,使用集合或數(shù)組的任何情況下都不能返回null

Item 44: Write doc comments for all exposed API elements

為所有暴露出去的API寫(xiě)文檔注釋


上一章:枚舉和注解
下一章:通用原則

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市潘拱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拧略,老刑警劉巖芦岂,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異垫蛆,居然都是意外死亡盔腔,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)月褥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人瓢喉,你說(shuō)我怎么就攤上這事宁赤。” “怎么了栓票?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵决左,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我走贪,道長(zhǎng)佛猛,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任坠狡,我火速辦了婚禮继找,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘逃沿。我一直安慰自己婴渡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布凯亮。 她就那樣靜靜地躺著边臼,像睡著了一般。 火紅的嫁衣襯著肌膚如雪假消。 梳的紋絲不亂的頭發(fā)上柠并,一...
    開(kāi)封第一講書(shū)人閱讀 52,457評(píng)論 1 311
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼臼予。 笑死鸣戴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瘟栖。 我是一名探鬼主播葵擎,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼半哟!你這毒婦竟也來(lái)了酬滤?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤寓涨,失蹤者是張志新(化名)和其女友劉穎盯串,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體戒良,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡体捏,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了糯崎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片几缭。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖沃呢,靈堂內(nèi)的尸體忽然破棺而出年栓,到底是詐尸還是另有隱情,我是刑警寧澤薄霜,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布某抓,位于F島的核電站,受9級(jí)特大地震影響惰瓜,放射性物質(zhì)發(fā)生泄漏否副。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一崎坊、第九天 我趴在偏房一處隱蔽的房頂上張望备禀。 院中可真熱鬧,春花似錦流强、人聲如沸痹届。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)队腐。三九已至,卻和暖如春奏篙,著一層夾襖步出監(jiān)牢的瞬間柴淘,已是汗流浹背迫淹。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留为严,地道東北人敛熬。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像第股,于是被迫代替她去往敵國(guó)和親应民。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360