IT戦記

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

Effective Java 読書会 8 日目 「それ enum で出来るよ」

はじめに

ビット演算? それE... - はてなちゃんセリフ - はてなセリフ

今日読んだところ

143 ページ〜 159 ページ

int 定数とか使わない

型安全じゃないから

enum 使う

enum Hoge {
    FOO, BAR;
}

簡単

メソッドも持てる

enum Hoge {
    FOO, BAR;

    void a() {
        /* (snip) */
    }
}

で、以下のように使う

Hoge.FOO.a();

シングルトンも

最近の Java では enum でやるらしい!

switch 文の代わりに固有メソッド

enum Hoge {
    FOO { void a() { /*snip*/ } },
    BAR { void a() { /*snip*/ } };

    abstract void a();
}

以下のコードが

// これはダメ
switch(obj) {
    case Hoge.FOO: /*snip*/ break;
    case Hoge.BAR: /*snip*/ break;
    default: throw new AssertionError();
}

以下で良くなる!

// case の書き忘れもなくて安心!
obj.a();

戦略 enum

enum の値をカテゴライズする

// たとえば
enum Hoge {
    FOO { void a() { /*snip*/ } }, // これと
    BAR { void a() { /*snip*/ } }, // これがまったく同じ処理だったとして
    BAZ { void a() { /*snip*/ } };

    abstract void a();
}

以下のようになる

enum Hoge {
    FOO(Strategy.STRATEGY_1),
    BAR(Strategy.STRATEGY_1),
    BAZ(Strategy.STRATEGY_2);

    private enum Strategy {
        STRATEGY_1 { void a() { /* ... */ } }, STRATEGY_2 { void a() /* ... */ }

        abstract void a();
    }

    // 戦略の委譲
    private final Strategy strategy;
    Hoge(Strategy strategy) { this.strategy = strategy; }
    void a() { strategy.a(); }
}

values 便利

for (Hoge v : Hoge.values()) { /* ... */ }

インスタンスの配列

ordinary は使うのはやめましょう

値が増えるとコンパイルの度に順番変わるので
private で id 的なフィールドを持てと

EnumSet 便利!

64 個までならビット演算(|)と比べても遜色のない速度で動く!テラすごす!!

Set<Hoge> set = EnumSet.of(Hoge.FOO, Hoge.BAR, Hoge.BAZ);  // ビット演算 FOO (=0x1) | BAR (=0x1 << 1) | BAZ (=0x1 << 2) と比べても遜色のない速度で動く

of メソッドかっけー!

EnumMap も便利

Enum の ordinal をインデックスにした配列じゃなく EnumMap を使おう

まとめ

enum すげー便利!めっちゃ使う!

Effective Java 読書会 6 日目 「ジェネリクス!」

はじめに

今日からついにジェネリクスに突入しました!

今日読んだところ

101 ページ〜 125 ページ

関数オブジェクト

インスタンスメソッドを一つだけ持っているクラスのオブジェクト。
C++ の関数ポインタのような、 JavaScriptクロージャのような使われ方をする。

ストラテジーパターンで

ストラテジーだけを渡すときに使う。

関数オブジェクトのインスタンスは複数いらない

以下のように、シングルトンにしたり、 public static final なフィールドに入れられたりする。

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Main {

    public static void main(final String[] args) {
        List<String> list = Arrays.asList("aaaa", "baaabaaaaaaa", "cacaca");
        Collections.sort(list, CountAComparator.INSTANCE);
        System.out.println(list);
    }
}

 enum CountAComparator implements Comparator<CharSequence> {

    INSTANCE;
    
    @Override
    public int compare(CharSequence cs1, CharSequence cs2) {
        return countA(cs1) - countA(cs2);
    }

    private int countA(CharSequence cs) {
        int count = 0;
        for (int i = 0; i < cs.length(); i++) {
            char c = cs.charAt(i);
            if (c == 'a' || c == 'A') {
                count++;
            }
        }
        return count;
    }
}

static メンバクラスに戦略を保持する

そういうやりかたもする

class Klass {
    pravate static class Comparator extends Comparator<String> {
        // snip
    }
    // snip
}

無名クラスとして使われたりもする

クロージャ

import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Main {

    public static void main(final String[] args) {
        List<Integer> list = Arrays.asList(1, 10, 100);
        
        final int base = 30;
        
        // 30 から近い順にソート
        Collections.sort(list, new Comparator<Integer>() {

            @Override
            public int compare(Integer o1, Integer o2) {
                return Math.abs(base - o1.intValue()) - Math.abs(base - o2.intValue());
            }
        });
        System.out.println(list);
    }
}

ネストしたクラスの 3 種類

  • static メンバクラス
  • 非 static メンバクラス
  • 無名クラス
    • インタフェースや抽象クラスをそのまま new して中に書く
    • インスタンスメソッド内で new されればエンクロージングインスタンスを持つ
    • static なメンバを持てない
  • ローカルクラス
    • 無名クラスのように、メソッド内に書く
    • 違いは、繰り返し使用できること

それぞれの用途まとめ

  • static メンバクラス
    • クラスの構成要素を作る
  • 非 static なメンバクラス
  • 無名クラス
    • 関数オブジェクトやプロセスオブジェクト
  • ローカルクラス
    • あまり使わない

エンクロージングインスタンス

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;

public class Main {
    public static void main(final String[] args) {
        JFrame f = new JFrame();
        JButton b = new JButton("click");
        f.getContentPane().add(b);
        f.pack();
        f.setVisible(true);
        
        b.addActionListener(new Hoge().createListener());
    }
}

class Hoge {

    public ActionListener createListener() {
        return new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println(data);
                // この場所で、
            }
        };
    }

    // ここの this.data にアクセスできている!!
    // ここの this をエンクロージングインスタンスという
    private final String data = "hoge";
}

修飾された this

エンクロージングインスタンスはクラス名.this (修飾されたthis)で取得できる

Hoge enclosingInstance = Hoge.this;

ジェネリック型の原型は使わない

せっかくのコンパイル時の型チェックが台無しになっちゃうので><

for-each ループすごい!

for (String s : list) { } みたいな書き方できるんだ!すげー!

原型を使う 2 パターン

  • クラスリテラル
    • List.class, Set.class
  • instanceof
    • obj instanceof List, obj instanceof Set

境界型ワイルドカード、非境界型ワイルドカード

? は非境界型ワイルドカード ? extends CharSequence, ? super String は境界型ワイルドカード

非境界型ワイルドカード

  • null 以外なにも入れられない
  • オブジェクトとしてしか受け取れない
import java.util.Arrays;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        foo(Arrays.asList(""));
    }

    public static void foo(List<?> e) {
        Object o = e.get(0);
        e.add(o); // コンパイルエラー
    }
}

境界型ワイルドカード

import java.util.Arrays;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        foo(Arrays.asList(""));
    }

    public static void foo(List<? extends CharSequence> e) {
        // CharSequence で受け取れる
        CharSequence o = e.get(0);
    }
}
import java.util.Arrays;
import java.util.List;

public class Main {

    public static void main(String[] args) {
        foo(Arrays.asList(""));
    }

    public static void foo(List<? super String> e) {
        // String で受け取れる
        e.add("");
    }
}

無検査警告を取り除く

@SuppressWarnings("unchecked")

ジェネリック型が配列に出来ない理由

もともと、配列が共変クラスだったことが問題。ジェネリックス型からは、それができなくなる。
リストを使うことでその問題を解決できる。

まとめ

明日は、予定があるのでまとめてる時間ないかもなー><

Effective Java 読書会 5 日目 「最後に見せたツンデレ」

はじめに

今日も継承三昧><

今日読んだところ

86 ページ〜 100 ページ

オーバーライド可能なメソッド自己利用(self-use)のドキュメント化

たとえば、 AbstractCollection#remove の以下の部分

This implementation iterates over the collection looking for the specified element.
この実装は、指定要素を探すために走査をします。
If it finds the element, it removes the element from the collection using the iterator's remove method.
要素が見つかればイテレータの remove メソッドを使って削除します。
Note that this implementation throws an UnsupportedOperationException if the iterator returned by this collection's iterator method does not implement the remove method and this collection contains the specified object.
このコレクションが指定要素を含んでいるけど、イテレータが remove を実装していない場合には UnsupportedOperationException を投げます。

AbstractCollection (Java Platform SE 6)

このように、 iterator クラスの remove メソッドの self-use を明確にしている。
で、以下のように iterator の remove メソッドを実装しなければ、 Collection の remove も使えないことになる。

import java.util.AbstractCollection;
import java.util.Collection;
import java.util.Iterator;

public class Main {
    
    public static void main(final String[] args) {
        
        // args に "hoge" が含まれるとして。
        
        final Collection<String> collection = new AbstractCollection<String>() {

            @Override
            public Iterator<String> iterator() {
                return new Iterator<String>() {

                    private int index = 0;

                    @Override
                    public boolean hasNext() {
                        return index < args.length;
                    }

                    @Override
                    public String next() {
                        return args[index++];
                    }

                    // remove を実装しない
                    @Override
                    public void remove() {
                        throw new UnsupportedOperationException();
                    }
                };
            }

            @Override
            public int size() {
                return args.length;
            }

        };

        // ここで UnsupportedException が投げられる
        collection.remove("hoge");
    }
}

他にも removeAll メソッドや clear の箇所でも iterator の remove を使ってる記述がある
あと size メソッドに関しても、 toArray メソッドで最適化のヒントとして使っていることが明記されている。
また、 AbstractCollection は addAll で add を使っていることを明記している。
なので、以下のように add をオーバーライドすると addAll が実装できることが分かる。

import java.util.AbstractCollection;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class Main {
    
    public static void main(final String[] args) {
        
        final Collection<String> collection = new AbstractCollection<String>() {

            private List<String> impl = new LinkedList<String>(Arrays.asList("hoge", "fuga", "piyo"));
            
            @Override
            public Iterator<String> iterator() {
                final Iterator<String> iterator = impl.iterator();
                
                return new Iterator<String>() {
                    
                    @Override
                    public boolean hasNext() {
                        return iterator.hasNext();
                    }

                    @Override
                    public String next() {
                        return iterator.next();
                    }

                    @Override
                    public void remove() {
                        iterator.remove();
                    }
                };
            }

            @Override
            public int size() {
                return impl.size();
            }

            // add を実装すれば
            @Override
            public boolean add(String str) {
                return impl.add(str);
            }
        };

        // addAll もちゃんと動く
        collection.addAll(Arrays.asList("foo", "bar", "baz"));
        
        System.out.println(collection);
    }
}

このように、オーバーライド可能なメソッドをクラスに用意する場合はそのメソッドの self-use を解説した詳細なドキュメントが必要になる。

self-use を記述したドキュメントがカプセル化を壊している

前項の例が示すように、 AbstractCollection の add と addAll の関係や clear と iterator の remove の関係は、実装手段のレベルで切り離せないものになってしまっている。
これは、カプセル化のメリットと相反している。

継承を前提としたクラスがやることは self-use のドキュメント化だけじゃない

サブクラス実装者が何を求めているかを深く考えなければならない。
自分でサブクラスを実装してみたり、誰かにサブクラスを実装してもらって、意見を聞くなどするべき。

継承を前提とするクラスがやってはいけないこと

オーバーライド可能なインスタンスメソッドを、コンストラクタ、 clone 、 readObject メソッドから呼び出してはいけない。
スーパーコンストラクタとして呼び出された時点で、サブクラスのフィールドは初期化されていないので、サブクラスでオーバーライドされたメソッドを呼ぶのはダメ。

継承を前提とするクラスが Serializable を実装するときにやらなければならないこと

readResolve, writeReplace メソッドを protected にする

self-use の排除

self-use を排除することも一つの選択肢

    public void hoge() {
        fuga(); // self-use !!!!
    }

    public void fuga() {
        System.out.println("hello"); 
    }

以下のようにすると self-use がなくなる! fuga 拡張されても hoge は安心!

    public void hoge() {
        fugaHelper();
    }

    // private なので継承される可能性はない
    private void fugaHelper() {
        System.out.println("hello"); 
    }

    public void fuga() {
        fugaHelper();
    }

組み合わせ爆発

機能 A, B, C があって

  • 機能 A, B を実装したいクラス
  • 機能 A, C を実装したいクラス
  • 機能 A, B, C を実装したいクラス

などなど、様々なニーズがあったとして
これをクラスの継承だけでやろうとすると組み合わせが大変なことになるという話。

インタフェースと骨格実装 (Skeletal Implementation) を提供する

たとえば、インタフェース Collection に対する AbstractCollection などがある。
Java の標準 API 以外にも何かないかなーと思って調べたら
Google Collections Library に Iterator の Skeletal Implementation があった。

import java.util.Iterator;

// Iterator の(後付け)Skeletal Implementation 
import com.google.common.collect.AbstractIterator;


public class Main {

    public static void main(final String[] args) {

        Iterator<String> it = new AbstractIterator<String>() {
            int n = 0;

            // computeNext だけ実装すれば良い
            @Override
            protected String computeNext() {
                if (n < 10) {
                    return Integer.toString(n++);
                }
                return endOfData();
            }
        };
        
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }
}

骨格実装を使った擬似多重継承

骨格実装から作ったインスタンスを private に持って、そのインタフェースを継承することで擬似的に多重継承できる!!おおお!
ってことで擬似多重継承をやってみた。。。。

import java.util.AbstractList;
import java.util.AbstractQueue;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Queue;


public class Main {

    private static final class VariableAsQueueAndList<E> implements Queue<E>, List<E> {
        E impl = null;
        
        // Queue の骨格実装から実装を作る
        Queue<E> queueImpl = new AbstractQueue<E>() {
        
            @Override
            public Iterator<E> iterator() {
                return null;
            }
    
            @Override
            public int size() {
                return impl == null ? 0 : 1;
            }
    
            @Override
            public boolean offer(E e) {
                if (impl == null) {
                    impl = e;
                    return true;
                }
                return false;
            }
    
            @Override
            public E peek() {
                return impl;
            }
    
            @Override
            public E poll() {
                E r = impl;
                impl = null;
                return r;
            }

        };

        // List の骨格実装から実装を作る
        List<E> listImpl = new AbstractList<E>() {

            @Override
            public E get(int index) {
                if (index != 0) {
                    throw new IndexOutOfBoundsException();
                }
                return impl;
            }

            @Override
            public int size() {
                return 1;
            }
            
            
        };
        
        // 以下転送ラッパー
        @Override
        public boolean add(E e) {
            return queueImpl.add(e);
        }
        
        @Override
        public E element() {
            return queueImpl.element();
        }
        
        @Override
        public boolean offer(E e) {
            return queueImpl.offer(e);
        }
        
        @Override
        public E peek() {
            return queueImpl.peek();
        }
        @Override
        public E poll() {
            return queueImpl.poll();
        }
        @Override
        public E remove() {
            return queueImpl.remove();
        }
        @Override
        public boolean addAll(Collection<? extends E> c) {
            return queueImpl.addAll(c);
        }
        @Override
        public void clear() {
            queueImpl.clear();
        }
        @Override
        public boolean contains(Object o) {
            return queueImpl.contains(o);
        }
        @Override
        public boolean containsAll(Collection<?> c) {
            return queueImpl.containsAll(c);
        }
        @Override
        public boolean isEmpty() {
            return queueImpl.isEmpty();
        }
        @Override
        public Iterator<E> iterator() {
            return queueImpl.iterator();
        }
        @Override
        public boolean remove(Object o) {
            return queueImpl.remove(o);
        }
        @Override
        public boolean removeAll(Collection<?> c) {
            return queueImpl.removeAll(c);
        }
        @Override
        public boolean retainAll(Collection<?> c) {
            return queueImpl.retainAll(c);
        }
        @Override
        public int size() {
            return queueImpl.size();
        }
        @Override
        public Object[] toArray() {
            return queueImpl.toArray();
        }
        @Override
        public <T> T[] toArray(T[] a) {
            return queueImpl.toArray(a);
        }

        
        @Override
        public void add(int index, E element) {
            listImpl.add(index, element);
        }

        @Override
        public boolean addAll(int index, Collection<? extends E> c) {
            return listImpl.addAll(index, c);
        }

        @Override
        public E get(int index) {
            return listImpl.get(index);
        }

        @Override
        public int indexOf(Object o) {
            return listImpl.indexOf(o);
        }

        @Override
        public int lastIndexOf(Object o) {
            return listImpl.lastIndexOf(o);
        }

        @Override
        public ListIterator<E> listIterator() {
            return listImpl.listIterator();
        }

        @Override
        public ListIterator<E> listIterator(int index) {
            return listImpl.listIterator(index);
        }

        @Override
        public E remove(int index) {
            return listImpl.remove(index);
        }

        @Override
        public E set(int index, E element) {
            return listImpl.set(index, element);
        }

        @Override
        public List<E> subList(int fromIndex, int toIndex) {
            return listImpl.subList(fromIndex, toIndex);
        }
    }
    
    public static void main(final String[] args) {
        
        VariableAsQueueAndList<String> queue = new VariableAsQueueAndList<String>();
        
        // Queue として push
        queue.offer("hoge");
        
        // List として get
        System.out.println(queue.get(0));
    }
}

なげえええええええ><
これ本当に便利なんでしょうか><

インタフェースは最初が肝心

インタフェースは、一回決めたら影響範囲が大きすぎて仕様変更できません><
よく考えて設計しましょう

定数だけを持つインタフェースはやめましょう

混乱を招くので

タグ付けクラスをするくらいなら継承使ったらいいじゃない><

デレた!!!

最後に

明日、明後日は私用があるので、次回更新は明後日以降になるかもしれません><

Effective Java 読書会 4 日目「最近はあまり継承とか使わないらしい」

はじめに

らんらーらららんらんらーん♪(なうしか)っと
( ´ー`)フゥー...(゚Д゚)ハッ!

嘘です><ちょっと言ってみたかっただけです><

今日読んだところ

67 ページ〜 85 ページ

情報隠蔽カプセル化

API 以外の情報をモジュール外から見えなくすること
余計なテストとか書かなくていいから楽だよね。

パッケージプライベート

同じ jar の同じパッケージからのみ参照できる

protected, public

完全公開 API として保守していかなければならないよ。
private とパッケージプライベートとは全然違うレベルよ

インスタンスフィールド

private にしる! 
public static final で、その参照先が不変クラスな場合に限り private じゃなくてもいい 
public static final で配列とか持たないようにね

ミューテーター

オブジェクトの状態を変更するようなメソッドをミューテーターっていうんだね。

不変クラス 5 条件

  • ミューテーターを持たない
  • クラスを final にする
  • すべてのフィールドを final にする
  • すべてのフィールドを private にする
  • 他のオブジェクトと可変オブジェクトを共有しない。(防御的コピーを使って)

不変クラスの利点

  • 作成したときの状態から変化することがないので。誰とその変数を共有しているか考えなくていい
    • たとえば、「この関数に渡したら、この関数の中で値が変わらないかなー」とか考えなくてもいい
    • がんがん共有できて気持ちいいい
  • 本質的にスレッドセーフ
    • 値が変わらないので、同期をとる必要がない。
  • ハッシュが生成時に決まる

不変クラスの生成

不変クラスは private コンストラクタ, static ファクトリーメソッドと相性がいい。
生成時に値を決めなきゃいけないので、 static ファクトリーメソッドでいろいろなファクトリーを作るといい

可変クラス作るな!

よっぽど正当な理由がないかぎり、不変クラスにしる!
セッターを作りたい衝動は、禁止です><

継承よりコンポジション

継承は、スーパークラスの実装を詳細に理解していないとすることができない。
80 ページの例は、最初何故ダメなのか分からなかったが、分かってみると非常に勉強になった。

転送クラス

あるインタフェースにおける Proxy クラスを作るためのスーパークラス
転送クラスを使えば、簡単に Proxy クラスが作れる。

SELF 問題

Proxy パターンでは、 Proxy 経由で Real なクラスのメソッドが呼ばれることが想定されているが。
Real なクラスが自分の参照を含むオブジェクトを返す場合、そのオブジェクトは Real なクラスのメソッドを直接呼び出すことになる。
これを SELF 問題という

SELF 問題の例

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;

// インターフェースがあって
interface AInterface {

    public ActionListener createListener();

    public void say();
}

// インターフェースを継承した実装
class A implements AInterface {

    // アクションリスナーを返す
    // このとき、アクションリスナーは this を参照している
    @Override
    public ActionListener createListener() {
        return new Listener(this);
    }

    // インスタンスメソッドの実装
    @Override
    public void say() {
        System.out.println("I'm A.");
    }
}

// クラス A の Proxy
class AProxy implements AInterface {

    private AInterface impl;
    
    public AProxy(A a) {
        impl = a;
    }

    @Override
    public ActionListener createListener() {
        return this.impl.createListener();
    }

    // say は Proxy で拡張する
    @Override
    public void say() {
        impl.say();
        System.out.println("But, I'm Proxy.");
    }
}

// アクションリスナーの実装
class Listener implements ActionListener {
    
    private AInterface parent;
    
    public Listener(AInterface parent) {
        this.parent = parent;
    }
    
    @Override
    public void actionPerformed(ActionEvent e) {
        parent.say();
    }
}

public class Main {

    public static void main(String[] args) {
        JFrame frame = new JFrame();
        JButton button = new JButton("Click me");
        frame.getContentPane().add(button);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
        
        AInterface a = new AProxy(new A());

        // ここでボタンにアクションリスナーを登録
        button.addActionListener(a.createListener());
    }

}

このような場合にボタンがクリックされたときに AProxy の say は呼ばれず。 A の say が呼ばれてしまう。
これを SELF 問題という。

それでも継承するときは

すべての B が A であるという関係が成り立つことを念入りに確認すべし。
(A がスーパークラス、 B がサブクラス)

まとめ

週末も勉強するぞー!

Effective Java 読書会 3 日目「それ Apache Commons で出来るよ」

はじめに

じゃっばじゃばにしてやんよー♪っと
( ´ー`)フゥー...(゚Д゚)ハッ!
どーん

今日読んだところ

45 ページ〜 66 ページ

hashCode 2 契約

  • シグニフィカントなフィールドを変更しなければ、いつ hashCode を呼び出しても値は変わらない
  • x.equals(y) が true の場合は、 x.hashCode() == y.hashCode() が true

equals をオーバーライドしたら hashCode もオーバーライドすべき

前述の 2 契約を守るために必要

x.equals(y) が false の場合

かならずしも、 x.hashCode() != y.hashCode() になる必要はないが、
値がバラけているほうが HashMap のキーとして使ったときのパフォーマンスがいい。

パフォーマンスについて

hashCode 関数のコストをケチって HashMap の性能が悪くなったら本末転倒

hashCode の作り方

適当なゼロでない定数に、シグニフィカントなフィールのハッシュ値

result = 31 * result + field.hashCode()

と言う感じで合体させていけばいい

プリミティブな値の hashCode

  • boolean
    • hashCode = x ? 0 : 1;
  • byte, char, short, int
    • hashCode = (int) x;
  • long
    • hashCode = (int) (x ^ (x >>> 32));
  • float
    • hashCode = Float.floatToIntBits(x);
  • double
    • long y = Double.doubleToLongBits(x); hashCode = (int) (y ^ (y >>> 32));

他の hashCode

  • null
    • hashCode = 0;
  • List
    • Arrays.hashCode(x);

でも結局 hashCode に関しては

ここまで読んであれだけど Apache Commons の HashCodeBuilder 使えし!
http://commons.apache.org/lang/api/org/apache/commons/lang/builder/HashCodeBuilder.html
まさに外道

あ、そういえば equals も

EqualsBuilder 使えし!
http://commons.apache.org/lang/api/org/apache/commons/lang/builder/EqualsBuilder.html
な、なんだってー!!

もちろん toString ビルダーもね!

ToStringBuilder 使えし!
http://commons.apache.org/lang/api/org/apache/commons/lang/builder/ToStringBuilder.html
まいりましたーーー!!!

たとえば、以下のようなデータクラス

public class AdministrativeDivision {

    private String unit;

    private String name;
    
    public AdministrativeDivision(String name, String unit) {
        this.unit = unit;
        this.name = name;
    }

    public void setUnit(String unit) {
        this.unit = unit;
    }

    public String getUnit() {
        return unit;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

ちょこちょこっといじって… 

public class AdministrativeDivision {

    // 略

    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this);
    }

    @Override
    public boolean equals(Object o) {
        return EqualsBuilder.reflectionEquals(this, o);
    }

    @Override
    public int hashCode() {
        return HashCodeBuilder.reflectionHashCode(this);
    }
}

はい!完成っと!
おおおおおおおおすげーリフレクション楽だー!

シグニフィカントなフィールドが特定されている場合は

以下のような感じ

public class AdministrativeDivision {

    // 略

    @Override
    public String toString() {
        return new ToStringBuilder(this).append("name", name).append("unit", unit).toString();
    }

    @Override
    public boolean equals(Object o) {
        if (o == null) { return false; }
        if (o == this) { return true; }
        if (o.getClass() != getClass()) { return false; }
        AdministrativeDivision rhs = (AdministrativeDivision) o;
        return new EqualsBuilder()
                 .appendSuper(super.equals(o))
                 .append(name, rhs.name)
                 .append(unit, rhs.unit)
                 .isEquals();
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder().append(name).append(unit).toHashCode();
    }
}

すげーよ! Apache Commons 便利すぎるよ!

clone

clone の中で new はしない。
super.clone で生成されたオブジェクトをイジって返す。
持っているリファレンスは clone して super.clone の結果にくっつけてやる。
とかとか。
てか、 clone って使わないよなー。という話に。
static ファクトリメソッドはやしてやったほうがいいんじゃないかという話になった。

compareTo 4 契約

  • signum(x.compareTo(y)) == -signum(y.compareTo(x))
    • x.compareTo(y) が例外をスローする場合は、 y.compareTo(x) も例外をスローしなければならない
    • x.compareTo(y) が例外をスローしない場合は、 y.compareTo(x) も例外をスローしてはいけない
  • (x.compareTo(y) > 0 && y.compareTo(z) > 0) が true なら x.comparetTo(z) > 0 も true (推移的)
  • x.compareTo(y) == 0 が true なら signum(x.comparetTo(z)) == signum(y.compareTo(z)) は必ず true
  • x.compareTo(y) == 0 が true なら x.equals(y) (これは、強く推奨されているが絶対ではない)

double, float の compareTo と equals

double, float は NaN や -0.0 のとき == や < や > などの演算子では、 equals 契約や compareTo 契約を守れないため
Double.equals, Float.equals, Double.compare, Float.compare を使わなければならない

引き算による実装

オーバーフローするのでダメよ。

っていうか

ざゎ…ざゎ…
@hikoma っくす 「CompareToBuilder 使えば」
http://commons.apache.org/lang/api/org/apache/commons/lang/builder/CompareToBuilder.html
きたー!

と言うわけで

今日は、 Apache Commons にじゃっばじゃばにされちゃいました>< 

Effective Java 読書会 2 日目 「equals night」

はじめに

今日もお疲れ気味な君の右足にローキックどーん
ってな感じで
どーん

今日読んだところ

20 ページ〜 44 ページ

とりあえず Eclipse をインストールしました!

わーわーパチパチ。
とりあえず、わからずに JavaEE エディションにしたら、
Super Java Hacker の @hikoma っくすに「普通ので良かったのにー」と言われちょっとしょんぼりーぬ(´・ω・` )
気をとりなおして

static イニシャライザ

public class Sample001 {

    public static final Date HOGE_DATE;

    static {
        HOGE_DATE = initDate();
    }

    // (snip)
}

みたいな?

static イニシャライザでのエラーってどうするのん?

握りつぶして、ロガーに出すのがいいと思うという話でした。

初回実行時初期化のダメなところ

まず、ダブルチェッキングとか煩雑な書き方になるってのはもちろんある。
それと、経験的にクラス A の初期化時にクラス B の値を見に行ってクラス B からまた A にという「初期化、卵と鶏問題」が発生しやすいという話も出た。
よppっぽどの理由が無い限り static イニシャライザ使えと

Office Lady?

え、 OL?
ああ、 0L ね。Zelo Long ね。
ああ、フォントが悪いなー

キャッシュのメモリリークに気を付けろ!

Servlet でメモリがどんどん増えてくときって、大概キャッシュの消し忘れだよね。
Stack や HashMap など、自分でメモリを管理するときは、要らなくなったオブジェクトの参照が繋がったまま増加していかないか注意する

いいヒーププロファイラってある?

あまり使ったことないなー。
こんど調べてみたいねって話になった。
ちょっと話はそれて、 jconsole 便利だよねって話もした

WeakHashMap って使ったことある?

あまり使わないなあ、と言う意見が多数。
たしかに GUI コンポーネントとかで、イベントターゲットを実装するときはいいかもなーと思った。

かなり、ありそうなパターンだなーっと。
そういう場合は、リスナーが WeakHashMap とかになってればいい

ファイナライザー

まず、使わない。普通は使わない。使ったことない。
finally 便利よね。
ファイナライザーァァァァガーディアアアアアアン!!!!ってかっこいいねって話になった
あと、 finalize って super のやつは自動で呼ばれないんですね。

equals

かるがるしくオーバーライドしたら。ダメ。絶対。
実際、そんなにオーバーライドするケースってないなーという話になった。

equals 4 契約

x != null && y != null && z != null の場合

  • x.equals(x) は必ず true (反射的)
  • x.equals(y) == y.equals(x) は必ず true (対称的)
  • x.equals(y) && y.equals(z) が true なら x.equals(z) は必ず true (推移的)
  • x.equals(y) はオブジェクト内の値が変わらない限り、何回呼び出しても同じ結果 (整合性)

たとえば

データベースからスケジュールのデータを持ってきたとして
そのスケジュールが持っている会議室オブジェクトと
他の同時刻にあるスケジュールが持っている会議室オブジェクトを比較するとしたら equals をオーバーライドしてやるべき?

やらないべき。roomA.equals(roomB) じゃなくて、 roomA.getID().equals(roomB.getID()) とやるべき。会議室の名前は、変わる可能性があるから

という話になったような気がしたけど、でもデータベース上の ID は一つだから
equals の実装が ID を使っていれば equals を使ってもいいような気がしてきた。
会議室の名前は significant なのかどうか。うーん
あれー。うーん。まだ理解出来ていない
週末の宿題にしようと思う

equals をオーバーライドしたら hashCode もオーバーライド

その件は次回

そのほかの話題

Maven の使い方を教えてもらった!
Maven は .m2 ディレクトリの育てゲーらしい

ちなみに

次回の読書会は明後日。明日は出席できない人が多いらしい。

ちょっと工作

なんか Eclipse を入れたのでちょっとプログラムを書いてみた。

キー検出

そのパソコン内でキーが押されたことを検出(Java がアクティブじゃなくてもいい)
キーロガーに使えるよ!!!(まて

package jp.co.cybozu.amachang;

import org.eclipse.swt.internal.win32.OS;

public class Key {
    
    /**
     * No constructor.
     * This is utility class.
     */
    private Key() {
        throw new AssertionError();
    }
    
    /**
     * Check key is pressed from previous calling.
     * @return true if key is pressed.
     */
    public static boolean isPressed() {
        for (int keyCode = 'A'; keyCode <= 'Z'; keyCode++) {
            
            final short state = OS.GetAsyncKeyState(keyCode);
            
            if ((state & 0x8000) != 0) {
                return true;
            }
        }
        return false;
    }
}
これを使って、パソコンを触っている間は、5 分置きに ping を打つアプリ作りました

在籍管理用にー

ジョブ管理して
package jp.co.cybozu.amachang;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;

import javax.xml.transform.TransformerException;

import org.apache.http.client.ClientProtocolException;
import org.xml.sax.SAXException;

public class Secretary {
    private HashMap<Job, Job> eventMap = new HashMap<Job, Job>();
    
    public interface Job {
        
        void run() throws ClientProtocolException, IOException, SAXException, TransformerException;

        boolean shouldRun(boolean isPressed, Date now, Date lastPressedTime);
    }
    
    public void reserve(Job job) {
        eventMap.put(job, job);
    }
    
    public void cansel(Job job) {
        eventMap.remove(job);
    }
    
    public void run() throws InterruptedException, ClientProtocolException, IOException, SAXException, TransformerException {
        Date lastPressedTime = new Date();
        while (true) {
            final boolean isPressed = Key.isPressed();
            final Date now = new Date();
            final Iterator<Job> iterator = eventMap.values().iterator();
            while (iterator.hasNext()) {
                final Job job = iterator.next();
                if (job.shouldRun(isPressed, now, lastPressedTime)) {
                    job.run();
                }
            }
            if (isPressed) {
                lastPressedTime = now;
            }
            Thread.sleep(100);
        }
    }
}
main
package jp.co.cybozu.amachang;

import java.io.IOException;
import java.util.Date;

import javax.xml.transform.TransformerException;

import org.apache.http.client.ClientProtocolException;
import org.xml.sax.SAXException;

import jp.co.cybozu.amachang.Secretary.Job;

public class PingTimeCard {


    /**
     * @param args
     * @throws InterruptedException 
     * @throws IOException 
     * @throws ClientProtocolException 
     * @throws TransformerException 
     * @throws SAXException 
     */
    public static void main(String[] args) throws InterruptedException, ClientProtocolException, IOException, SAXException, TransformerException {
        Secretary secretary = new Secretary();
        
        secretary.reserve(new Job () {

            private Date lastPingTime = new Date();

            @Override
            public void run() throws ClientProtocolException, IOException, SAXException, TransformerException {
                lastPingTime = new Date();
                System.out.println("ping at " + lastPingTime + ".");
                TimeCard.ping();
            }

            @Override
            public boolean shouldRun(boolean isPressed, Date now, Date lastPressedTime) {
                return isPressed && (now.getTime() - lastPingTime.getTime() > 300000);
            }
            
        });
        
        secretary.run();
    }
}

まとめ

明後日もがんばるぞー!

Effective Java 読書会 1 日目「static メソッドの使い方などなど」

はじめに

社内で「Effective Java を 20 日で読もう」という勉強会が企画されて、今日がその一日目。
ノリノリな感じで。
どーん

今日読んだところ

5 ページ 〜 19 ページ

という訳で

いろいろ出た話題を残しておく
毎日続けるために、なるべく適当に書いていこうと思う
あと、自分が勝手に解釈してる部分も多々あると思うます><

static ファクトリーメソッド

static ファクトリーメソッドとは、コンストラクタの代わりに使われる static メソッドのことらしい。

コンストラクタは、名無し

たしかに String コンストラクタとかごちゃごちゃしてて一個一個名前付けたほうがいいんじゃね?と思うなーと
http://java.sun.com/javase/6/docs/api/java/lang/String.html#constructor_summary

String string = new String(stringBuffer);

より

String string = String.in(stringBuffer);

のほうが分かりやすい的な? in はさすがにダメですかね><

オブジェクトの生成を省ける場合がある

インスタンス制御クラスは、たぶん enum でやるよねーってことでスルー。
でも Collections.emplyList() 的な使いかたはありっすね。

戻り値の型のサブタイプも返せる

これは static ファクトリーに関わらず便利なことが多い。

パラメタ化された型を推論してくれる
// new は String を推論してくれない(String を二回書かなければいけない)
List<String> stringList = new LinkedList<String>();
// static ファクトリは戻り値の型から推論してくれる(Arrays.<String>asList() が呼ばれる)
List<String> stringList = Arrays.asList();
static ファクトリーメソッドを使う場合は、名前でそれと分かるようにしたほうがいいよ

newInstance とか getInstance とか of とか valueOf とか asList とかとか newList とか

Builder パターンかっこいい

Node anchorElement = new HTMLElement.Builder("a").href("http://www.google.com/").title("Google").build();

みたいな感じですかね!
かっちょいー。

Singleton はテストしにくい件について

そこら辺は、 DI 使うと楽だよねって話になった。 @Singleton って書くだけだし、楽チンらしい。

何 Exception 使っていいか分かりません><

とりあえず、いつも IllegalArguments にしてる自分は負け組み><
後々の章で詳しく出てくるらしい。

enum ってコンストラクタ持てるの?

持てる。余裕。そして便利
インスタンス制御クラスもシングルトンも enum

Singleton なクラスにするか、Utility クラスにするか

コンストラクタが必要なら Singleton 確定。
あと、状態を持つ場合は Singleton だよね、クラスに状態を持つのは変だよねという話になった。

まとめ

明日も頑張るぞー。おー。