Point2D.Floatにコピーコンストラクタがない理由

ことの発端

後輩からTwitterでこんなリプライが届いた(@nekomatuより)

Pointは引数にPointを与えると同じ位置にセットしてくれるコンストラクタがあるのに、Point2D.floatにはない。仕方ないので、.xと.yをそれぞれ普通に渡してるんだけど。。。 なんか意味があってできないようにしてあるのかなぁ? @tatesuke

ほう。なるほど。確かにjava.awt.PointクラスにはPoint(Point)コンストラクタが存在するのに、Point2D.FloatやPoint2D.Doubleには存在しない。この質問を見た瞬間の第一印象は

知らね、設計思想の違いじゃね?ホジホジ(σ? ̄)

と言った感じw
でもよく見るとPointクラスもPoint2D.FloatクラスもPoint2D.DoubleクラスもPoint2Dクラスのサブクラスなんです。親子クラス間でしかも言語の標準ライブラリで設計思想が違うってのはなんでなのかなと思い、ちと調べてみることに。

理由1 導入順序

いきなり結論ですが、PointクラスにコピーコンストラクタがあってPoint2D.FloatクラスやPoint2D.Doubleクラスにないのは歴史的観点からでしょう。というのも、PointクラスのJavadocを見ると導入されたバージョンは1.0です。そして、Point2DクラスのJavadocを見ると、不思議なことにPointクラスの親クラスでかかわらず導入されたバージョンは1.2からなんですね。つまり、Pointクラスは元々あって、後により汎用的なPoint2Dクラスが導入されたときに継承関係が生まれたようです。その時、コピーコンストラクタは不要と判断されたのでしょう。

不要なら無くしてしまえばいいじゃないかという気もしますが、それはそれで下位互換性とか色々考慮すべきことはありますからね。僕も「Deprecatedにしてしまえばいいのに」とか思いましたが非推奨と不要は別物ですもんね。

理由2 Cloneableである

コピーコンストラクタは不要とはいえ、毎回

Point2D.Float p2 = new Point2D.Float(p1.x, p1.y);

などと書くのは面倒です。なにかいい方法はないのか!?はい。あります。上にも書いたようにPointクラス、Point2D.Floatクラス、Point2D.Doubleクラスは全てPoint2Dクラスのサブクラスです。そしてPoint2DクラスはCloneableインタフェースを実装しています*1。つまり、インスタンスをコピーするならcloneメソッドを使ってくださいってことですよ*2

Point2D.Float p2 = p1.clone();

理由3 cloneメソッドは(多分)早い

なんでcloneなのさ。コンストラクタを準備してもいいじゃないか。という意見もありそうですが、ここはもっとじっくりと探ってみましょう。Point2D型のcloneメソッドはこんな実装になっています。

/* java.awt.geom.Point2D#clone() */
public Object clone() {
    try {
        return super.clone();
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError();
    }
}

って、親クラスのcloneメソッドを呼んでるだけですね。じゃぁ親クラスのcloneメソッドを覗いてみましょう。Point2Dクラスの親クラスは・・・Objectクラスでした。

/* java.lang.Obect#clone() */
protected native Object clone() throws CloneNotSupportedException;

おっと、ここで行き止まりです。とまぁ、見ての通りcloneメソッドってnativeなんですね。nativeってことは言葉通りネイティブコードが走るわけで、つまりは処理速度が早いってことです。あまり詳しくありませんが、コンストラクタを呼び出せばJVMがメモリを確保して、フィールドを初期化して・・・などという処理が挟まってそれなりに重たい処理であることは想像がつきます。そんな面倒なことはすっ飛ばして実行速度が早いネイティブコードを走らせて軽々とコピーしてしまおうという発想のようです*3。これは確かに正しい選択ではないでしょうか。座標を扱うプログラムはゲームプログラミングとかシミュレーション関係のそれなりに速度を求められるものが多いでしょうから。

まとめ

というわけで、PointクラスにコピーコンストラクタがあるのにPoint2D.FloatやPoint2D.Doubleには存在しない理由は

  1. 歴史的にPointクラスのほうが先にあった
  2. 代わりにcloneメソッドを使いましょう(早いから)

ってことですか。今まできにしたことなかったけど、調べてみると色々あるもんですなー。面白い。

*1:実装とは言え、Cloneableインタフェース自体はメソッドの存在しないマーカインタフェースなんですけどねヽ(´ー`)ノ

*2:cloneメソッドは浅いコピー、深いコピーに気を付けなければいけませんが、いずれのクラスもメンバはプリミティブ型のx, yだけなので心配ありませぬ

*3:実際は実装依存なので本当に軽々なのかは知りません。だからタイトルも「(多分)早い」。