平々毎々(アーカイブ)

はてなダイアリーのアーカイブです。

covariant method return type

ときどきの雑記帖経由。

covariant method return type はあまり知られていない Java の機能です
(JLSの Section 8.4.5を参照してください)。
基本的には、サブクラスでオーバーライドされているときに
メソッドの retrun type を narrow することを Java は許可します。
例をあげましょう:

public class Foo {
}

public class SubFoo extends Foo {
}

public class Bar {
    final Foo foo = new Foo();
    public Foo getFoo() {
        return foo;
    }
}

public class SubBar extends Bar {
    final SubFoo subFoo = new SubFoo();

    @Override
    public SubFoo getFoo() {
        return subFoo;
    }
}

たしかにC#にはない機能。
同じようなことをやる場合はnewキーワードによる隠蔽を使うのが普通なのだけど、次のコードはコンパイルできない。

public class Foo {}

public class SubFoo : Foo {}

public class Bar
{
    readonly Foo foo = new Foo();
    public virtual Foo Foo
    {
        get { return foo; }
    }
}

public class SubBar : Bar
{
    readonly SubFoo subFoo = new SubFoo();
    public override Foo Foo
    {
        get { return subFoo; }
    }
    public new SubFoo Foo
    {
        get { return subFoo; }
    }
}

コンパイルできないのは、パブリックプロパティFooが被ってしまうため。

というわけで、インターフェース経由にして、インターフェースの明示的実装を使うしかない。
Foo型を返すプロパティをインターフェース経由にしたければ、こう。

public class Foo {}

public class SubFoo : Foo {}

public interface IBar
{
    Foo Foo { get; }
}

public class Bar : IBar
{
    readonly Foo foo = new Foo();
    public Foo Foo
    {
        get { return foo; }
    }
}

public class SubBar : IBar
{
    readonly SubFoo subFoo = new SubFoo();
    public SubFoo Foo
    {
        get { return subFoo; }
    }
    Foo IBar.Foo
    {
        get { return this.Foo; }
    }
}

SubFoo型を返すプロパティをインターフェース経由にしたければ、こう。

public class Foo {}

public class SubFoo : Foo {}

public interface IBar<T> where T : Foo
{
    T Foo { get; }
}

public class Bar : IBar<Foo>
{
    readonly Foo foo = new Foo();
    public virtual Foo Foo
    {
        get { return foo; }
    }
}

public class SubBar : IBar<SubFoo>
{
    readonly SubFoo subFoo = new SubFoo();
    public override Foo Foo
    {
        get { return ((IBar<SubFoo>)this).Foo; }
    }
    SubFoo IBar<SubFoo>.Foo
    {
        get { return subFoo; }
    }
}

ま、ややこしいです。しかし元記事にも「これが必要となる局面はめったにありません」とあるので、少しくらいややこしくても許容範囲かなと。
使用例としてあげてあるApache Wicket frameworkのような状況なら、1番目のようなインターフェースの使い方でいいでしょう。たぶん。