Open35

Effective Javaメモ

nmemotonmemoto

項目22 型を定義するためだけにインターフェースを使う

Hidden comment
nmemotonmemoto

項目21 将来のためにインターフェースを設計する

Hidden comment
Hidden comment
nmemotonmemoto

項目20 抽象クラスよりもインタフェースを選ぶ

Hidden comment
Hidden comment
Hidden comment
Hidden comment
Hidden comment
nmemotonmemoto

項目88 防御的にreadObjectメソッドを書く

Hidden comment
nmemotonmemoto

項目60 正確な答えが必要ならば、floatとdoubleを避ける

Hidden comment
nmemotonmemoto

項目61 ボクシングされた基本データよりも基本データ型を選ぶ

Hidden comment
nmemotonmemoto

項目62 他の型が適切な場所では、文字列を避ける

nmemotonmemoto
  • 文字列は貧弱
    • 他の値型に対する代替として
      • 数値だったらint, float, BigInteger
      • 適切な型がなかったら適切な値型を書くべき
    • 列挙型に対する代替として
      • enum使おう
    • 集合型に対する代替として
      • 複数の構成要素を持つ集合のキーで文字列で表現するのは、だめ
      • private staticのメンバークラスを書く
    • ケイパビリティに対する代替として
      • 独自実装のThreadLocalの例
        • 「二つの独立したクライアントが、それぞれのスレッドローカル変数に同じ名前を使うと決めたら、両方のクライアントは変数を意図せずに共有してしまい、一般には両方のクライアントがエラーになります。」がわからない。
Hidden comment
nmemotonmemoto

項目45 ストリームを注意して使う

nmemotonmemoto
  • ストリームパイプライン

    • ソースのストリーム、それに続く0個以上の中間操作、その後に一つの終端操作から構成される
    • 遅延して評価される
      • 評価は終端操作が呼び出されるまで開始されない
      • 終端操作を完了させるために必要のないデータ要素は計算されない
    • デフォルトで順次実行される
      • 並行実行する方法はparallelメソッドを呼び出す
        • そうするのが適切なことはめったにありません(項目48 ストリームを並列化するときは注意を払う)
  • ストリームAPI

    • 流れるようなAPI
      • パイプラインを構成するすべての呼び出しをつなげて単一の式となるように設計されている
    • 十分に汎用性があるので、事実上はストリームを使ってどのような計算もできる
    • いつストリームを使うかに関して鉄則はないが、発見的な規則はある
nmemotonmemoto
  • アナグラムのプログラム例
    • 入力例
      listen
      silent
      enlist
      inlets
      banana 
      nabana
      cat
      act
      tac 
      dog
      god
      
    • 出力例
      4: [enlist, inlets, listen, silent]
      3: [act, cat, tac]
      2: [banana, nabana]
      2: [dog, god]
      
    • マップへのキー挿入でcomputeIfAbsentを使っている
      • キーに複数の値を関連付けするマップの実装を単純にしている
  • ストリーム乱用の例
    • alphabetizeメソッドの処理も単一の式となっている
nmemotonmemoto
  • ストリームパイプラインの可読性にとっては以下が重要
    • 明示的な型がないので、ラムダのパラメータを注意深く命名すること
    • ヘルパーメソッドを使うこと
  • char値にストリームを使うのは控えるべき
  • ストリームを使うことに意味がある場合にだけ使うこと
    • 関数オブジェクトでできない、かつコードブロックでできること
      • スコープ内のローカル変数を読みだしたり修正できる
      • return, break, continue, 例外スローができる
    • ストリームに適した処理
      • 均一に要素のシーケンスを変換する
      • 要素のシーケンスをフィルターする
      • 単一操作を使って要素のシーケンスをまとめる
      • 共通の属性でグループ化する
        • Collectors.groupingBy
      • 条件を満足する要素の検索
    • ストリームで行うのが困難
      • パイプラインの複数ステージから対応する要素を同時にアクセスすること
      • 値を他の値にマッピングしてしまうと、元の値は失われる
        • 回避策: ペアオブジェクト 、マッピングを逆にする
nmemotonmemoto
  • メルセンヌ素数の例(マッピングを逆にするの例)
    • メルセンヌ素数とは、メルセンヌ数の中で素数になるもの
    • メルセンヌ数とは、2のべき乗から1を引いた数
      • n=0のとき、0
      • n=1のとき、1
      • n=2のとき、3(メルセンヌ素数)
      • n=3のとき、7(メルセンヌ素数)
      • n=4のとき、15
      • n=5のとき、31(メルセンヌ素数)
    • 最初の20個のメルセンヌ素数を表示するプログラムの例
      • Stream.iterateで2を開始点として、その次の素数を生成し続ける
      • filterで素数判定、limitで20個に制限
    • 指数も表示させたい
      • メルセンヌ数の指数はバイナリ表現のビット数→マッピングを逆にする
nmemotonmemoto

項目46 ストリームで副作用のない関数を選ぶ

nmemotonmemoto
  • 純粋関数: 結果が入力だけに依存している関数
  • 「これを達成するには、中間操作と終端操作の両方のストリーム操作に渡される関数オブジェクトには副作用がないべき」
    • なんか日本語がおかしい気がする。達成したいんだったら純粋関数でなければいけないのでは。
      -コード中の悪臭
    • 状態を更新するラムダ(副作用あり、純粋関数でない)
    • ストリームが行った計算結果を表示する以外の処理を行っているforEach操作
  • forEach操作
    • 最も力を持たない終端操作の一つ
    • 最もストリーム向きではない
    • 明示的なループであり、その結果並列化に適していない
    • ストリームの計算結果を報告するためだけに使い、計算を行うために使うべきではない
    • 計算結果を既存のコレクションへ追加するといった他の目的のために使うことには意味がある
    • (コメント)大半は戻り値ないことの言い換え
    • (コメント)順序を持つものに対してはその順序通りループする仕様のことを明示的なループとよんでいる?
nmemotonmemoto
  • Collectors
    • https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/stream/Collectors.html
    • リダクション戦略: ストリームのまとめ方
    • ストリームをまとめた結果できるのがコレクションなので、まとめ方がコレクター
      • toList()
      • toSet()
      • toCollection(collectionFactory)
        • 任意のコレクション型への終端処理が可能
    • staticインポートするのが慣習
      • ストリームパイプラインが読みやすくなる
  • topTenの処理
nmemotonmemoto
  • toMapの使い方
    • 第一引数: ストリームをキーにマッピングする関数, 第二引数: ストリームを値へマッピングする関数
      • ストリームの複数の要素が同一のキーにマッピングされると、パイプラインはIllegalStateExceptionをスロー
    • 第三引数: マージ関数 or 最後の書き込みを優先する方針を強制する
    • 第四引数: 特定のマップ実装の指定(EnumMap, TreeMap)
    • toConcurrentMapもあるよ
  • groupByの使い方
    • 第一引数: 分類関数
      -分類関数は要素を受け取り、それが分類されるカテゴリを返す
    • 第二引数: ダウンストリームコレクター
      • カテゴリー内のすべての要素を含むストリームから値を生成する
        • (コメント)集約操作の際に、集約されたグループ内に処理を行うコレクターのこと
        • toSet()
        • toColletction(collectionFactory)
        • counting()
          • 他にもいっぱいある
            • Streamのコレクターにもあるダウンストリームコレクター
              • counting, summing, averaging, summarizingで始まる名前の9個のメソッド、reducingメソッドのすべてのオーバーロード、filtering,,,,,,,
            • それ以外のCollectorsのメソッド
              • minBy, maxBy, joining
    • 第三引数: マップファクトリ
      • 第二引数に先行している
        • 例: 値がTreeSetであるTreeMapを返す
          Map<Integer, TreeSet<String>> result = list.stream()
          .collect(Collectors.groupingBy(
            String::length,                                // 第1引数: グループ化するキー
            Collectors.toCollection(TreeSet::new),         // 第2引数: ダウンストリームコレクター
            TreeMap::new                                   // 第3引数: TreeMapを生成
          ));
          
    • groupingByConcurrentもあるよ
    • partitioningByもあるよ
nmemotonmemoto

項目31 APIの柔軟性向上のために境界ワイルドカードを使う

この項目は、共変・反変の関係を使ってAPIの柔軟性を表現しよう。Javaだとその手段は境界ワイルドカードと言っている。

nmemotonmemoto
  • パラメータ化された型は不変
    • String <: Object, Integer <: Numberは成立する
    • List<String> <: List<Object>, List<Integer> <: List<Number> は成立しない
    • (脱線)不変とは
      • 共変でも反変でもないもの
        • 共変: A <: Bのとき、T<A> <: T<B>の関係になるT
        • 反変: A <: Bのとき、T<A> :> T<B>の関係になるT

以下はEが不変のため変換できない。

public void pushAll(Iterable<E> src) {
    for (E e : src)
        push(e);
}

Stack<Number> numberStack = new Stack<>();
Iterable<Integer> integers = ....;
numberStack.pushAll(integers);
public void popAll(Collection<E> dst) {
    while (!isEmpty())
        dst.add(pop());
}

Stack<Number> numberStack = new Stack<Number>();
Collection<Object> objects = ....;
numberStack.popAll(objects);
  • 境界ワイルドカード型
    • Iterable<? extends E>
      • EのなんらかのサブタイプのIterable
    • Collection<? super E>
      • Eのなんらかのスーパータイプのコレクション
  • PECS
  • (脱線)Scalaすごい
    • ジェネリクスを使うとき、共変クラス・反変クラス・非変クラスを定義できる
nmemotonmemoto
  • unionの例
    • 項目30(ジェネリックメソッド)の例を改善
    • 戻り値として境界ワイルドカード型を使うな
      • クライアントのコードでワイルドカード型を使うことを強制する
    • Java8より前は目的型が必要だった
  • maxの例
    • 型パラメーターに対してワイルドカードを適用した例
      • Comparable<? super T>とするのはその型もしくはその型のスーパータイプという範囲で比較可能である表現をするため
    • 何か恩恵をもたらすのでしょうか。はい、もたらします。(なぜなら、以下なので)
      • ScheduledFutureがComparable<ScheduledFuture>を実装していない。
      • ScheduledFuture <: Delayed かつ Comparable<Delayed>が実装されている。
nmemotonmemoto
  • 型パラメータとワイルドカードの二重性
    • swapメソッドの2つの宣言
    • (公開する宣言において、)型パラメータがメソッド宣言中に一度しか現れないなら、それをワイルドカードで置き換えてください
      • 理由: 単純だから
    • ワイルドカードのままだとコンパイルできない
      • ワイルドカードキャプチャのためのprivateヘルパーメソッドをジェネリックメソッドを使って実装する