キャストの仕組み

昨日のつづき。


昨日の日記に対してodzさんがコメントしてくれたみたいで、TがObjectになってしまう理由は分かった。以下、自分の理解。

  • 仮にTがDuck型しか受け取らないにしても他の型(例えばFoo型)も受け取れる可能性がある
  • TがDuck型しか受け取っていない実装ならTをDuckに推論しても良いが、そういう仕組みはない
  • コンパイル後の生成物で実装が複数できるわけでなく1つしかできない
  • 仮に推論してTをDuckに置き換えても、後から別ファイルでFooの定義を追加し、そこからFooのインスタンスでfoo()を呼び出したとき問題が起こる。foo()を定義している箇所をリコンパイルしないといけなくなる
  • 結果的に推論せず全てObjectで置き換えるだけしか不可能(ワイルドカードなどを使用しない限り)


つまりTと書くとObject型にしかなり得ない。


しかし分からないのは、どういう仕組みでキャストが挿入されるのかがいまいち分からない。例えば以下のようなコードを作成してみた。

class Duck {
    public void quack() { System.out.println("gaaa"); }
}

class Foo {
    public void quack() { System.out.println("...."); }
}

class test {
    public static <T> void func(T ducky) {
        Duck ducky1 = ducky;
        ducky.quack();
    }

    public static void main(String[] args) {
        func(new Duck());
        //func(new Foo());
    }
}

func()メソッドの内部でducky1にduckyを代入しているので、そのタイミングでキャストを挿入してくれると思った。しかし、コンパイル時に以下のエラーが出る。

test.java:11: 互換性のない型
検出値  : T
期待値  : Duck
                Duck ducky1 = ducky;
                              ^
test.java:12: シンボルを見つけられません。
シンボル: メソッド quack()
場所    : java.lang.Object の クラス
        ducky.quack();
             ^
エラー 2

明らかにキャストを入れてくれていない。ではどういうタイミングでキャストが入るのだろうか?