Guava學(xué)習(xí)之Joiner

本文是對 Guava 中 Joiner 的學(xué)習(xí)介紹。歡迎加入學(xué)習(xí)項目: LearningGuava喊暖。

使用示例

以下參考:官方文檔伊约。

開發(fā)過程中嘁信,用分隔符連接字符串序列可能是一個比較繁瑣的過程,但本不應(yīng)該如此抡爹。Joiner 可以簡化這個操作掩驱。

如果序列中包含 null 值,那么可以使用 Joiner 跳過 null 值:

    // 跳過 null 值
    result = Joiner.on("; ").skipNulls().join("Harry", null, "Ron", "Hermione");
    Assert.assertEquals(result, "Harry; Ron; Hermione");

也可以通過 useForNull(String) 來將 null 值替換為指定的字符串冬竟。

    // 替換 null 值
    result = Joiner.on("; ").useForNull("null").join("Harry", null, "Ron", "Hermione");
    Assert.assertEquals(result, "Harry; null; Ron; Hermione");

同樣可以在對象上使用 Joiner,最終會調(diào)用對象的 toString() 方法欧穴。

    // 使用在對象上,會調(diào)用對象的 toString() 函數(shù)
    result = Joiner.on(",").join(Arrays.asList(1, 5, 7));
    Assert.assertEquals(result, "1,5,7");

對于 Map ,可以使用這樣的代碼:

    // MapJoiner 的使用泵殴,將 map 轉(zhuǎn)換為字符串
    Map map = ImmutableMap.of("k1", "v1", "k2", "v2");
    result = Joiner.on("; ").withKeyValueSeparator("=").join(map);
    Assert.assertEquals(result, "k1=v1; k2=v2");

源碼分析

以下參考:Guava 是個風(fēng)火輪之基礎(chǔ)工具(1)涮帘。

初始化方法

Joiner 的構(gòu)造方法被設(shè)置成了私有,需要通過靜態(tài)的 on(String separator) 或者 on(char separator) 函數(shù)初始化笑诅。

拼接基本函數(shù)

Joiner 了中最為核心的函數(shù)就是 <A extends Appendable> A appendTo(A appendable, Iterator<?> parts)调缨。作為全功能函數(shù)疮鲫,其它所有的字符串拼接最終都會調(diào)用這個函數(shù)。

  public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
    checkNotNull(appendable);
    if (parts.hasNext()) {
      appendable.append(toString(parts.next()));
      while (parts.hasNext()) {
        appendable.append(separator);
        appendable.append(toString(parts.next()));
      }
    }
    return appendable;
  }

這段代碼的分析如下:

  • 這里的 Appendable 源碼中傳入的是實現(xiàn)該接口的 StringBuilder弦叶。
  • 因為是公共方法俊犯,無法保證 appendable 值不為空,所以要先檢查該值是否為空湾蔓。
  • if ... while ... 的結(jié)構(gòu)確保末尾不會添加多余的分隔符瘫析。
  • 通過本地 toString 方法,而不是直接調(diào)用對象的 toString 方法默责,這種做法提供了空指針保護贬循。

不可能發(fā)生的異常

在源碼中,有個地方的處理值得關(guān)注一下:

   public StringBuilder appendTo(StringBuilder builder, Iterator<? extends Entry<?, ?>> entries) {
      try {
        appendTo((Appendable) builder, entries);
      } catch (IOException impossible) {
        throw new AssertionError(impossible);
      }
      return builder;
    }

這里之所以 IOException 的變量名取名為 impossible 是因為:雖然 Appendable 接口的 append 方法會拋出 IOException桃序,但是傳入的 StringBuilder 在實現(xiàn)的時候并不會拋出改異常杖虾,所以為了適應(yīng)這個接口,這里不得不捕捉異常媒熊。這樣捕捉后的斷言處理也就可以理解了奇适。

巧妙的可變長參數(shù)轉(zhuǎn)換

有一個添加的重載函數(shù)如下所示:

 public final <A extends Appendable> A appendTo(
      A appendable, @NullableDecl Object first, @NullableDecl Object second, Object... rest)
      throws IOException {
    return appendTo(appendable, iterable(first, second, rest));
  }

其中 iterable 函數(shù)將參數(shù)變?yōu)橐粋€可以迭代的序列,該函數(shù)如下所示芦鳍。

private static Iterable<Object> iterable(
      final Object first, final Object second, final Object[] rest) {
    checkNotNull(rest);
    return new AbstractList<Object>() {
      @Override
      public int size() {
        return rest.length + 2;
      }

      @Override
      public Object get(int index) {
        switch (index) {
          case 0:
            return first;
          case 1:
            return second;
          default:
            return rest[index - 2];
        }
      }
    };
  }

通過實現(xiàn) AbstractList 的方式嚷往,巧妙地復(fù)用了編譯器生成的數(shù)組,減少了對象創(chuàng)建的開銷柠衅。這樣的實現(xiàn)需要對迭代器有深入的了解皮仁,因為要確保實現(xiàn)能夠滿足迭代器接口各個函數(shù)的語義。

Joiner 二次創(chuàng)建

因為 Joiner 創(chuàng)建后就是不可更改的了菲宴,所以為了實現(xiàn) useForNullskipNulls 等語義贷祈,源碼會再次創(chuàng)建一個匿名類,并覆蓋相應(yīng)的方法喝峦。

useForNull 函數(shù)匯中為了防止重復(fù)調(diào)用 useForNullskipNulls势誊,還特意覆蓋了這兩個方法,一旦調(diào)用就拋出運行時異常谣蠢。為什么不能重復(fù)調(diào)用 useForNull 粟耻?因為覆蓋了 toString 方法,而覆蓋實現(xiàn)中需要調(diào)用覆蓋前的 toString眉踱。

  public Joiner useForNull(final String nullText) {
    checkNotNull(nullText);
    return new Joiner(this) {
      @Override
      CharSequence toString(@NullableDecl Object part) {
        return (part == null) ? nullText : Joiner.this.toString(part);
      }

      @Override
      public Joiner useForNull(String nullText) {
        throw new UnsupportedOperationException("already specified useForNull");
      }

      @Override
      public Joiner skipNulls() {
        throw new UnsupportedOperationException("already specified useForNull");
      }
    };
  }

skipNulls 函數(shù)實現(xiàn)如下所示勋颖。個人比較奇怪的是 skipNulls 中為什么不禁止重復(fù)調(diào)用 skipNulls 函數(shù)。

  public Joiner skipNulls() {
    return new Joiner(this) {
      @Override
      public <A extends Appendable> A appendTo(A appendable, Iterator<?> parts) throws IOException {
        checkNotNull(appendable, "appendable");
        checkNotNull(parts, "parts");
        while (parts.hasNext()) {
          Object part = parts.next();
          if (part != null) {
            appendable.append(Joiner.this.toString(part));
            break;
          }
        }
        while (parts.hasNext()) {
          Object part = parts.next();
          if (part != null) {
            appendable.append(separator);
            appendable.append(Joiner.this.toString(part));
          }
        }
        return appendable;
      }

      @Override
      public Joiner useForNull(String nullText) {
        throw new UnsupportedOperationException("already specified skipNulls");
      }

      @Override
      public MapJoiner withKeyValueSeparator(String kvs) {
        throw new UnsupportedOperationException("can't use .skipNulls() with maps");
      }
    };
  }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末勋锤,一起剝皮案震驚了整個濱河市饭玲,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌叁执,老刑警劉巖茄厘,帶你破解...
    沈念sama閱讀 216,544評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件矮冬,死亡現(xiàn)場離奇詭異,居然都是意外死亡次哈,警方通過查閱死者的電腦和手機胎署,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,430評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來窑滞,“玉大人琼牧,你說我怎么就攤上這事“溃” “怎么了巨坊?”我有些...
    開封第一講書人閱讀 162,764評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長此改。 經(jīng)常有香客問我趾撵,道長,這世上最難降的妖魔是什么共啃? 我笑而不...
    開封第一講書人閱讀 58,193評論 1 292
  • 正文 為了忘掉前任占调,我火速辦了婚禮,結(jié)果婚禮上移剪,老公的妹妹穿的比我還像新娘究珊。我一直安慰自己,他們只是感情好纵苛,可當(dāng)我...
    茶點故事閱讀 67,216評論 6 388
  • 文/花漫 我一把揭開白布剿涮。 她就那樣靜靜地躺著,像睡著了一般赶站。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上纺念,一...
    開封第一講書人閱讀 51,182評論 1 299
  • 那天贝椿,我揣著相機與錄音,去河邊找鬼陷谱。 笑死烙博,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的烟逊。 我是一名探鬼主播渣窜,決...
    沈念sama閱讀 40,063評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼宪躯!你這毒婦竟也來了乔宿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,917評論 0 274
  • 序言:老撾萬榮一對情侶失蹤访雪,失蹤者是張志新(化名)和其女友劉穎详瑞,沒想到半個月后掂林,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,329評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡坝橡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,543評論 2 332
  • 正文 我和宋清朗相戀三年泻帮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片计寇。...
    茶點故事閱讀 39,722評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡锣杂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出番宁,到底是詐尸還是另有隱情元莫,我是刑警寧澤,帶...
    沈念sama閱讀 35,425評論 5 343
  • 正文 年R本政府宣布贝淤,位于F島的核電站柒竞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏播聪。R本人自食惡果不足惜朽基,卻給世界環(huán)境...
    茶點故事閱讀 41,019評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望离陶。 院中可真熱鬧稼虎,春花似錦、人聲如沸招刨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,671評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽沉眶。三九已至打却,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谎倔,已是汗流浹背柳击。 一陣腳步聲響...
    開封第一講書人閱讀 32,825評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留片习,地道東北人捌肴。 一個月前我還...
    沈念sama閱讀 47,729評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像藕咏,于是被迫代替她去往敵國和親状知。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,614評論 2 353

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

  • Scala與Java的關(guān)系 Scala與Java的關(guān)系是非常緊密的D醪椤饥悴! 因為Scala是基于Java虛擬機,也就是...
    燈火gg閱讀 3,440評論 1 24
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn)铺坞,斷路器起宽,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法济榨,內(nèi)部類的語法坯沪,繼承相關(guān)的語法,異常的語法擒滑,線程的語...
    子非魚_t_閱讀 31,623評論 18 399
  • 面向?qū)ο笾饕槍γ嫦蜻^程腐晾。 面向過程的基本單元是函數(shù)。 什么是對象:EVERYTHING IS OBJECT(萬物...
    sinpi閱讀 1,054評論 0 4
  • 做個不糾結(jié)的人 受家里環(huán)境以及父母的教育影響丐一,在買東西上面我一直是個比較糾結(jié)的人藻糖,我媽喜歡一個東西喜歡的不行,總是...
    桃紙是一種餅閱讀 267評論 0 0