IT戦記

プログラミング、起業などについて書いているプログラマーのブログです😚

Effective Java 読書会 10 日目 「Java の基本テクニック集」

はじめに

読書会に参加していないところがあるので、そこは議事録を読みながら、なるべく自分の言葉で書いていきます!

読んだところ

175 ページ〜 222 ページ

引数の検査をきちんとして javadoc の @throws に書く

などは、事前に引数チェックして出す。たとえば、 OpenJDK の String(byte[], int, int, String) では、以下のような実装になっている、自分で引数チェックをして、その内容を明確に @throws に記述している。

    // チェック関数
    private static void checkBounds(byte[] bytes, int offset, int length) {
        if (length < 0)
            throw new StringIndexOutOfBoundsException(length);
        if (offset < 0)
            throw new StringIndexOutOfBoundsException(offset);
        if (offset > bytes.length - length)
            throw new StringIndexOutOfBoundsException(offset + length);
    }

    /**
     * (snip)
     * 
     * (以下のように javadoc に書く)
     *
     * @throws  IndexOutOfBoundsException
     *          If the {@code offset} and {@code length} arguments index
     *          characters outside the bounds of the {@code bytes} array
     *
     * @since  JDK1.1
     */
    public String(byte bytes[], int offset, int length, String charsetName)
        throws UnsupportedEncodingException
    {
        if (charsetName == null)
            throw new NullPointerException("charsetName");

        checkBounds(bytes, offset, length); // ここで引数チェック

        // (snip)
    }

受け取ったらすぐにチェック!

以下の場合は引数チェックしない

  • コストがデカい
  • 引数のチェックが、自体が元々やりたい処理の一部の場合は 2 回チェックすることになるので、やらなくていい

防御的コピー

不変オブジェクトが理想的だけど、可変オブジェクトを自分以外から破壊されないようにコピーしてしまうことも重要。
これも簡単な話で、OpenJDK の java.lang.String の実装では、以下のように char 配列を防御的にコピーしている。他のオブジェクトからの変更可能性を排除して、自分は不変オブジェクトでいられる!

    public String(char value[], int offset, int count) {
        if (offset < 0) {
            throw new StringIndexOutOfBoundsException(offset);
        }
        if (count < 0) {
            throw new StringIndexOutOfBoundsException(count);
        }
        // Note: offset or count might be near -1>>>1.
        if (offset > value.length - count) {
            throw new StringIndexOutOfBoundsException(offset + count);
        }
        this.offset = 0;
        this.count = count;

        // 防御的コピー!!!!
        this.value = Arrays.copyOfRange(value, offset, offset+count);
    }

メソッド名

Java の標準ライブラリのメソッド名などを参考に、いい名前にしましょう

便利なメソッドは要望が出てから

最初から、便利なメソッドを作り過ぎない><
シンプルなものだけ提供して、使われる箇所を一通り見てから追加したければする。

引数多すぎ自重

4 つまで。
引数の減らし方

  • メソッド分割
  • ヘルパークラス
  • ビルダーパターン
    • かこいいやつ!
  • 引数に HashMap や TreeMap などのコンクリートクラスを使うより、 Map とかインタフェースを使う
  • boolean より 2 値の enum
    • enum! enum! みんな腕をふれー!

困惑させるオーバーロードは禁止

同じ引数の数のオーバーロードは避けたらいいんじゃね。
自動ボクシングで話がややこしくなります。

可変長引数

引数がひとつ必須の場合は

static int sub(int ... args) { /* ... */ }

じゃなくて

static int sub(int first, int ... args) { /* ... */ }

配列を表示する

System.out.println(Arrays.toString(array)); // これが正解

System.out.println(Arrays.asList(array)); // こうじゃないよ、これは可変長引数だからね

可変長引数はあまり乱用しない

不適切に使用すると、困惑させちゃうから
static void foo(Object ... args) とかはダメよ☆

配列やコレクションを返すメソッドで null を返すな

空配列や空コレクションを返すように!
ちなみに空配列は不変オブジェクトなので、使いまわせるので軽い。

javadoc

書け

変数スコープはちっちゃくね

  • 変数宣言は変数が使われるところで書く
  • 宣言時初期化
  • while より for、 for より for-each
    • Iterable を実装すれば、なんでも for-each!
  • メソッドもちっちゃくね

とにかく変数の使われるところをはっきりさせる

ライブラリを使え

ライブラリを使え
車輪の再発明するなー!!

float, double は 0.1 を正確に表現できない

BigDecimal 使えと
丸め方まで制御可能

ボクシングされたデータ型に == はダメ

あたりまえだけど、よくある間違い

文字列結合して意味を持たせるのはダメ

面倒でもクラス作る

文字列結合

StringBuilder 使いましょう StringBuffer は無駄に synchronized なので遅い

あるならインタフェース使う

以下より

ArrayList<Foo> = new ArrayList<Foo>();

以下のほうがいい。

List<Foo> = new ArrayList<Foo>();

あとから修正が楽

まとめ

今回は、多めですが内容が簡単な章でした!