インターフェイスにより同一メソッドが複数表れたときの挙動

スーパークラスインターフェイスのメソッド名が同一のとき

interface I {
    public abstract void m();
}

abstract class Super {
    public abstract void m();
}

class Sub extends Super implements I {
    public void m() {
        System.out.println("hoo");
    }
}
public class Test {
    public static void main(String[]args) {
        new Sub().m();
    }
}

問題なくコンパイル&実行される。

インターフェイスのメソッドはスーパークラスですでに実装済みの時

interface I {
    public void m();
}

class C {
    public void m() {
        System.out.println("hoo");
    }
}

class Sub extends C implements I {
}

public class Test {
    public static void main(String[]args) {
        new Sub().m();
    }
}

これも問題なくコンパイル&実行される。

同じインターフェイススーパークラスとサブクラスで重複して現れたとき

interface I {
    public abstract void m();
}

class C implements I {
    public void m() {
    }
}

class Sub extends C implements I {
}
public class Test {
    public static void main(String[]args) {
        new Sub().m();
    }
}

またまたこれも問題ない

ダイヤモンド継承もどき

interface I {
    public abstract void m();
}

interface I2 extends I {
    // public abstract void m(); //ここにこれがあっても同じ
}

interface I3 extends I {
    // public abstract void m(); //ここにこれがあっても同じ
}

class C implements I2, I3 {
    public void m() {
        System.out.println("hoo");
    }
}

public class Test {
    public static void main(String[]args) {
        new C().m();
    }
}

問題ないよ

まとめ

結論としては上で挙げた4つのパターンともに問題なくコンパイル&実行できる。
要はインターフェイスは「public void m()」を実装してくれと宣言しているだけなので、「implements」したクラスは自分自身かスーパークラスが実装をしてくれていれば、問題なく動く。
というか、これらのケースで問題が起こらないようにするために、Javaでは多重継承が禁止された筈なので、問題がおこったら困る。

JavaとC++の微妙な違い

JavaC++で微妙に異なっている仕様についてメモしてみる

余剰演算子「%」の適用範囲

C++では余剰演算子に適用できるのは整数型のみ

// C++
std::cout << 10   % 3 << std::endl;   //OK
std::cout << 10.0 % 3 << std::endl;   //NG
std::cout << 10   % 3.0 << std::endl; //NG
std::cout << 10.0 % 3.0 << std::endl; //NG

Javaだと浮動小数点でも可能

// Java
System.out.println(10   % 3);   //OK
System.out.println(10.0 % 3);   //OK
System.out.println(10   % 3.0); //OK
System.out.println(10.0 % 3.0); //OK

という訳でこんな結果も取れる

// java
System.out.println(9.4 % 1.3234234); //出力結果は0.13603620000000016

switchに使える型

実はJavaではlongに対してswitchはできない

// Java
char  x=10; //OK
byte  x=10; //OK
short x=10; //OK
int   x=10; //OK
long  x=10; //NG
switch(x) {
    case 10: System.out.println("10だよ"); break;
    case 20: System.out.println("20だよ"); break;
}

条件判定するときにint型に暗黙的型変換されるから、longではナローイング変換となり、エラー

enum

// C++
class X {
public:
  enum E{
    ET_1,
    ET_2,
    ET_3,
  };
};

int main()
{
  X::E e = X::ET_1; // 違い1
  switch(e) {
       // ↓違い2
    case X::ET_1: std::cout << "ET_1" << std::endl; break;
    case X::ET_2: std::cout << "ET_2" << std::endl; break;
    case X::ET_3: std::cout << "ET_3" << std::endl; break;
  }

  return 0;
}
// Java
class X {
    public enum E {
        ET_1,
        ET_2,
        ET_3,
    }
}

public class Test {

    public static void main(String[]args) {
        X.E e = X.E.ET_1; // 違い1
        switch(e) {
              // ↓違い2
            case ET_1: System.out.println("ET_1"); break;
            case ET_2: System.out.println("ET_2"); break;
            case ET_3: System.out.println("ET_3"); break;
        }
    }
}
違い1
enum型の要素を使用するときに、型名が必要かどうか
違い2
caseの中に書くときに、要素名だけでよいかどうか

まとめ

こういう細かいところで違ってたりすると混乱するねぇ