[Effective Java] Item 27: Favor generic methods

類似于Item 26里優(yōu)先考慮泛型礁蔗,Item 27告訴我們要優(yōu)先考慮泛型方法骆膝。

編寫泛型方法和編寫泛型類相似惶傻。泛型方法的特性是,無需明確指定類型參數(shù)的值,編譯器通過檢查方法參數(shù)的類型來計算類型參數(shù)的值鲫剿。類型推導(dǎo)鳄逾。

下面我們來詳細(xì)說明。

泛型方法的使用

我們先來看下面一個例子灵莲,這個method目的在于返回在傳入的兩個set里任意出現(xiàn)的元素:

// uses raw types - unacceptable! (Item 28)
public static Set union(Set s1, Set s2) {
  Set result = new HashSet(s1);
  result.addAll(s2);
  return result;
}

上面這段代碼雕凹,我們能夠編譯成功,但是會有兩個warning:

Union.java:5: warning: [unchecked] unchecked call to HashSet(Collection<? extends E>) as member of raw type HashSet
    Set result = new HashSet(s1);

Union.java:6: warning: [unchecked] unchecked call to HashSet(Collection<? extends E>) as member of raw type HashSet
    result.addAll(s2);

要解決這些warning政冻,我們需要定義三個set(兩個傳入值枚抵,一個返回值)的類型。我們可以使用泛型明场。

// generic method
public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
  Set<E> result = new HashSet<E>(s1);
  result.addAll(s2);
  return result;
}

使用泛型后汽摹,warning都沒有了。我們可以用類似下面的代碼使用這個union method榕堰。

// simple program to exercise generic method
public static void main(String[] args) {
  Set<String> set1 = new HashSet<>(Arrays.asList("A"));
  Set<String> set2 = new HashSet<>(Arrays.asList("B"));
  Set<String> res = union(set1, set2);
}

Type Interface

在上面的代碼中竖慧,當(dāng)我們調(diào)用union,我們不需要直接指定參數(shù)類型逆屡,compiler會自動處理圾旨。比如compiler看到上面union里面?zhèn)魅氲氖莾蓚€Set<String>,就知道E這個泛型在這里實際指的是String魏蔗,那么返回值自然就是Set<String>砍的。這個過程就是Type Interface。

把三個set都定義為泛型有一個缺點莺治,就是三個set的實際類型必須一致廓鞠。比如傳入的兩個set都必須存String,返回的set也必須存String谣旁。

因為有type interface床佳,在使用constructor定義一個parameter的時候,我們能減少很多不必要的參數(shù)定義榄审。比如:

// parameterized type instance creation with constructor
Map<String, List<String>> anagrams = new HashMap<String, List<String>>();

上面的代碼里面右邊HashMap里的類型定義其實是冗余的砌们。

而在Java里,Map里有一個method:

public static <K, V> HashMap<K, V> newHashMap() {
    return new HashMap<K, V>();
}

這個method使用了generic static factory method搁进。generic static factory method里面使用到了type interface浪感。因為這個method,anagrams的定義可以簡化成:

// parameterized type instance creation with static factory
Map<String, List<String>> anagrams = new HashMap<>();

Generic Singleton Factory

由此我們引出一個相關(guān)的概念饼问,叫g(shù)eneric singleton factory(泛型單例工廠)影兽。

有時候我們希望能創(chuàng)造出一個適用于很多不同類型的,immutable的object莱革。因為泛型是有擦除來實現(xiàn)峻堰,我們可以寫一個static factory method來實現(xiàn)讹开。這種方法常用在function object里面,比如Collections.reverseOrder茧妒。

我們來看下面的例子:

// generic singleton factory pattern
private static UnaryFunction<Object> IDENTITY_FUNCTION = new UnaryFunction<Object>() {
  public Object apply(Object arg) {
    return arg;
  }
}

// in Java 8, can be written as
private static UnaryFunction<Object> IDENTITY_FUNCTION = (t) -> t;

// IDENTITY_FUNCTION is stateless and its type parameter is unbounded so it's safe to share one instance across all types
// use @SuppressWarnings because we have warning due to converting Object to T
@SuppressWarnings("unchecked")
public static <T> UnaryFunction<T> identityFunction() {
  return (UnaryFunction<T>) IDENTITY_FUNCTION;
}

實際在Java中萧吠,F(xiàn)unction已經(jīng)實現(xiàn)了identity()左冬。

它的源碼是:

/**
 * Returns a function that always returns its input argument.
 *
 * @param <T> the type of the input and output objects to the function
 * @return a function that always returns its input argument
 */
 static <T> Function<T, T> identity() {
   return t -> t;
 }

使用identityFunction的代碼:

String[] strings = { "jute", "hemp", "nylon" };
UnaryOperator<String> sameString = identityFunction();
for (String s : strings) {
  System.out.println(sameString.apply(s));
}

Number[] numbers = { 1, 2.0, 3L };
UnaryOperator<Number> sameNumber = identityFunction();
for (Number n : numbers) {
  System.out.println(sameNumber.apply(n));
}

Recursive Type Bound

如果一個參數(shù)類型被含自身的表達(dá)式限制(a type parameter to be bounded by some expression involving that type parameter itself)桐筏,那么這個參數(shù)類型就是recursive type bound。

最常見和recursive type bound一起使用的是Comparator這個interface拇砰。比如:

// using a recursive type bound to express mutual comparability
public static <T extends Comparable<T>> T max(List<T> list) {...}

其中<T extends Comparable<T>>梅忌,可以讀成 for every type T that can be compared to itself

我們來看看它的使用:

// returns the max value in a list - uses recursive type bound
public static <T extends Comparable<T>> T max(List<T> list) {
  Iterator<T> i = list.iterator();
  T result = i.next();
  while (i.hasNext()) {
    T t = i.next();
    if (t.compareTo(result) > 0) {
      result = t;
    }
  }
  return result;
}

雖然recursive type bound比較復(fù)雜除破,但好在并不常用牧氮。我們只需要簡單了解一下就好。


Reference

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瑰枫,一起剝皮案震驚了整個濱河市踱葛,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌光坝,老刑警劉巖尸诽,帶你破解...
    沈念sama閱讀 216,997評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異盯另,居然都是意外死亡性含,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評論 3 392
  • 文/潘曉璐 我一進(jìn)店門鸳惯,熙熙樓的掌柜王于貴愁眉苦臉地迎上來商蕴,“玉大人,你說我怎么就攤上這事芝发⌒魃蹋” “怎么了?”我有些...
    開封第一講書人閱讀 163,359評論 0 353
  • 文/不壞的土叔 我叫張陵辅鲸,是天一觀的道長格郁。 經(jīng)常有香客問我,道長瓢湃,這世上最難降的妖魔是什么理张? 我笑而不...
    開封第一講書人閱讀 58,309評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮绵患,結(jié)果婚禮上雾叭,老公的妹妹穿的比我還像新娘。我一直安慰自己落蝙,他們只是感情好织狐,可當(dāng)我...
    茶點故事閱讀 67,346評論 6 390
  • 文/花漫 我一把揭開白布暂幼。 她就那樣靜靜地躺著,像睡著了一般移迫。 火紅的嫁衣襯著肌膚如雪旺嬉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,258評論 1 300
  • 那天厨埋,我揣著相機與錄音邪媳,去河邊找鬼。 笑死荡陷,一個胖子當(dāng)著我的面吹牛雨效,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播废赞,決...
    沈念sama閱讀 40,122評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼徽龟,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了唉地?” 一聲冷哼從身側(cè)響起据悔,我...
    開封第一講書人閱讀 38,970評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎耘沼,沒想到半個月后极颓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,403評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡耕拷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,596評論 3 334
  • 正文 我和宋清朗相戀三年讼昆,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片骚烧。...
    茶點故事閱讀 39,769評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡浸赫,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出赃绊,到底是詐尸還是另有隱情既峡,我是刑警寧澤,帶...
    沈念sama閱讀 35,464評論 5 344
  • 正文 年R本政府宣布碧查,位于F島的核電站运敢,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏忠售。R本人自食惡果不足惜传惠,卻給世界環(huán)境...
    茶點故事閱讀 41,075評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望稻扬。 院中可真熱鬧卦方,春花似錦、人聲如沸泰佳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,705評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至浇坐,卻和暖如春睬捶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背近刘。 一陣腳步聲響...
    開封第一講書人閱讀 32,848評論 1 269
  • 我被黑心中介騙來泰國打工擒贸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人跌宛。 一個月前我還...
    沈念sama閱讀 47,831評論 2 370
  • 正文 我出身青樓酗宋,卻偏偏與公主長得像积仗,于是被迫代替她去往敵國和親疆拘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,678評論 2 354

推薦閱讀更多精彩內(nèi)容