C#のメソッド戻り値や可視性が共変(Covariance)でないのが地味ーに窮屈
近ごろお手製ライブラリのJava版とC#版のあいだを行き来していて、その中で地味ーに窮屈だと感じているのが、「C#のメソッド戻り値や可視性が共変(Covariance)でない」ということです。
※一応註記しておくと、ここで「戻り値の共変」と述べているのはジェネリクスの型パラメータに関してではありません。戻り値の型そのものです。つまりList<Animal>
をList<Cat>
にできないと言っているのではなく、List<Animal>
を(例えば)LinkedList<Animal>
にできないと言っているのです。
前提
まず前提として以下のようなクラス継承関係があるとします:
問題
Javaではメソッド戻り値と可視性はいずれも共変なので、以下のようにSupplierB#supply()
メソッドの戻り値をSuppliedB
クラス・インスタンスにしたり、可視性をpublic
に変更したりできます。そのようにしてもSupplierB
は依然としてSupplierA
としても妥当であると考えられます:
public class SupplierA { SuppliedA supply() { /* ... */ }; } public class SupplierB extends SupplierA { public SuppliedB supply() { /* ... */ }; } SuppliedA sa = new SupplierB();
このようなことはC#の世界ではできません。以下のようなコードはSupplierB#Supply()
メソッドの戻り値についても可視性についてもコンパイル・エラーとなります:
public class SupplierA { internal virtual SuppliedA Supply() { /* ... */ }; } public class SupplierB : SupplierA { public override SuppliedB Supply() { /* ... */ }; } SuppliedA sa = new SupplierB();
SupplierB#Supply()
の宣言にnew
キーワードをつければエラーはなくなりますが、これはもはやオーバーライドではなく、SupplierB
インスタンスを(SuppliedA
型で宣言された変数に代入するなどして)SupplierA
型として参照するとそのSupply()
メソッドはSuppliedA
クラスで宣言されたものになってしまいます。