ITコンサルの日常

ITコンサル会社に勤務する普通のITエンジニアの日常です。

同一シグネチャのメソッドを持った別々のインタフェースを実装するとどうなるか

プログラミングC# 第四版 p166

タイトルからして分かりづらいですが、要はJavaでいうところのこういうことです。

  • 同じシグネチャ(public void func())を持った
  • 別々のインタフェース(IFunc1とIFunc2)を
  • 実装したクラス(FuncImpl)はどうなるか。

ということですが、結果どうにもなりませんでした。

public class test
{
        public static void main(String[] args) throws Exception
        {
                new test();
        }

        public test()
        {
                FuncImpl f = new FuncImpl();
                f.func();
                IFunc1 if1 = f;
                if1.func();
                IFunc2 if2 = f;
                if2.func();

                System.out.println(IFunc1.f);
                System.out.println(IFunc2.f);

                // Error
                //System.out.println(FuncImpl.f);
        }

        interface IFunc1
        {
                public static final int f = 10;
                public void func();
        }

        interface IFunc2
        {
                public static final int f = 20;
                public void func();
        }

        class FuncImpl implements IFunc1, IFunc2
        {
                public void func()
                {
                        System.out.println("FuncImpl#func");
                }
        }
}

結果はこう。

>java test
FuncImpl#func
FuncImpl#func
FuncImpl#func
10
20

>

メソッドについては、どっちのインタフェースを採用しても同じのため、結局どっちでもいいということらしい。

9.4.1 継承及び上書き

同じメソッド宣言が一つのインタフェースから継承される経路は,複数存在することがある。この事実は,いかなる困難も引き起こさず,決してそのままではコンパイル時エラーを生じない。

http://www.y-adagio.com/public/standards/tr_javalang/9.doc.htm#40247
この辺の記述がそうなのか?あいまいだけど。
ちなみに、定数フィールドについては、各インタフェースで別々の値を持つことができるので、どっちでもいいということはないです。

9.3.2.1 あいまいな継承フィールド

例えば直接的スーパインタフェース内の二つが同じ名前をもつフィールドを宣言しているために同じ名前をもつ二つのフィールドが一つのインタフェースによって継承されれば,一つのあいまいなメンバ(ambiguous members)のいかなる使用も,コンパイル時エラーを生ずる。

http://www.y-adagio.com/public/standards/tr_javalang/9.doc.htm#40247
ちなみにC#では、Javaのようにすることも出来ますが、それぞれのインタフェースごとに実装することも可能です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            FuncImpl f = new FuncImpl();
            //f.func();
            IFunc1 if1 = f;
            if1.func();
            IFunc2 if2 = f;
            if2.func();
        }
    }

    interface IFunc1
    {
        void func();
    }

    interface IFunc2
    {
        // Javaと違って、インタフェースに定数は入れられません。。
        // private const int f = 10;
        void func();
    }

    class FuncImpl : IFunc1, IFunc2
    {
        //public void func()
        //{
        //    Console.WriteLine("FuncImpl#func called.");
        //}

        void IFunc1.func()
        {
            Console.WriteLine("FuncImpl#IFunc1.func called.");
        }

        void IFunc2.func()
        {
            Console.WriteLine("FuncImpl#IFunc2.func called.");
        }
    }
}

結果はこう。

FuncImpl#IFunc1.func called.
FuncImpl#IFunc2.func called.

名前空間ってことを考えると、こっちの方が言語仕様として正しいような気もします。