Generic 再考

JavaのGenericの便利さに気づいて以来、Genericの技術はメリット、デメリット共に注目してました。
最近、Wicket1.4のGeneric対応でもめたりしてたのもあって、もう一度Genericについてじっくり勉強してみました。

IBMの以下の記事なんか見てると、ちょうどこの前発見した共変(covariant)って単語が結構出てくる。
http://www.ibm.com/developerworks/jp/java/library/j-jtp04298.html
http://www.ibm.com/developerworks/jp/java/library/j-jtp07018.html

共変(covariant)という単語自体の理解がだいぶ足りなさそうなので、まずはここから攻めてみた。
covariant / contravariant /invariant を調べてると、Wikipedia経由でぶつかったのがこちら

Say you have a class Foo, which has a method bar(). Method bar() takes an argument of type middle_type, and returns a value of type middle_type.
Now you make a subclass of Foo called SubFoo, and you override bar(). What types can the new bar() take? What types can it return?

 Look at return types first: 
 we want to be able to substitute SubFoo where existing code expects Foo, 
 so it needs to return things of type middle_type, or of some subtype (e.g. sub_type). This should be pretty obvious.

 As for what types our new bar() can take: 

 One answer is: 
 bar() can take only things of type middle_type. 
 You can't declare it to take sub_type, and you can't declare it to take super_type. 
 This is called invariance.

 Another answer: 
 bar() can only be declared to take things that are a subtype of middle_type
   so middle_type is OK, and sub_type is OK, but super_type is out. 
 This is called covariance.

 Finally, the third answer: 
 bar() can only be declared to take things that are a supertype of middle_type
   so any of middle_type, and super_type may be passed to bar(), but sub-type is not allowed. 
 This is called contravariance. 

親クラスFoo, 子クラスSubFoo があって、SubFooがbarメソッドをOverrideしているときに、barメソッドの引数として
  • middle_typeしかとれないケースをinvariance
  • middle_typeとsub_typeがとれるケースをcovariance
  • middle_typeとsuper_typeがとれるケースをcontracovariance
と定義するそうです。

以下のようなコードの場合、
 class Foo<T> {
     void bar(T m) {
         System.out.println("Foo is called");
     }
 }

 class SubFoo<T> extends Foo<T> {

     @Override
     void bar(T m) {
         System.out.println("SubFoo is called");
     }
 }
インスタンスを生成する時にパラメータタイプとして<? super MiddleType>や<? extends MiddleType>を駆使すればcovarianceやcontravarianceが実現で来そうです。
 Foo<? super MiddleType> foo1_1 = new  SubFoo<MiddleType>();
 Foo<? super MiddleType> foo1_2 = new  SubFoo<SuperType>();
 Foo<? extends MiddleType> foo2_1 = new SubFoo<MiddleType>();
 Foo<? extends MiddleType> foo2_2 = new SubFoo<SubType>();
ただ、このままだとbarメソッドの使い道がいまいち腑に落ちないんですよね。
引き続き調査です。

Comment

  1. Saxman
    2008-08-07 Thu 02:00

    >barメソッドの使い道がいまいち腑に落ちないんですよね。
    foo,barで代名詞化(抽象化?)されてるので、どのように腑に落ちないのか分からないのですが、どんな使い道があるの?ってことでしょうか?

    確かに難しいですよね。
    型の汎用性を付けるために無くてはならないものだと思うのですが、逆になくてもいいやん(Generics使わなければ)とも考えられるので。
    なんかいい例がないか考えてみます。