interface

D の interface は Java のと違う。
Java の interface は Object として扱える。

interface I {
}

class InterfaceTest implements I {
  private int id;
	
  InterfaceTest(int id) { this.id = id; }
  public static void main(String[] args) {
    InterfaceTest i = new InterfaceTest(1);
    System.out.println(i.toString());
    System.out.println(i.hashCode());
  }
}

...
InterfaceTest@131f71a
20051738

上記のようにからっぽの interface でも、Object のメソッドを実行できる。
Dでは下記のようなコードはコンパイルが通らない。

interface I {
}

class C : I {
  int id;
  this(int id) { this.id = id; }
}

import std.stdio;
void main() {
  I i = new C(1);
  writefln(i.toString());
  writefln(i.toHash());
}

interface 経由で Object のメソッドを呼び出そうとしてもできない。
全てのクラスは Object のサブクラスであるから、Object のメソッドを実装しているのにも関わらず、である。
D の interface は、C++のクラスと同様クラス階層のルートにあるようなイメージである。
Java のは

interface ObjectInterface {
  String toString();
  boolean equals(Object obj);
  int hashCode();
  void wait();
  ...
}

といった interface を暗黙のうちに継承している感じである。ユーザー定義かライブラリかを問わず、Java の interface はクラス階層のルートにはない。


D と Java の interface のどちらかが意味論的に正しいか、適切なのかは私には判断できない。設計思想が違うし、一長一短ありだろうからメリットの方を選択したと言われればそれまでである。

だが、実際に使ってて便利なのは Java の方である。なかんずく、D の方では toString() メソッドが呼び出せないのは痛い。


しかし、なければ作ればいいのだ。
上記の D のコードの interface 部分を

interface ObjectInterface {
  char[] toString();
  uint toHash();
  int opEquals(Object o);
  int opCmp(Object o);
  void print();
}

interface I : ObjectInterface {
}

とすれば、Java のコードと同様、Object で定義されているメソッドを呼び出してくれる。
そして具体クラスでは ObjectInterface のメソッドを実装する必要はない。Object がデフォルトの実装を提供してくれているからだ。「結果として」interface のメソッドが実装されていればコンパイルも実行も問題なくできる。


クラス独自の実装が欲しければもちろん、メソッドをクラス内で実装すればよい。

import std.string;
class C : I {
  int id;
  this(int id) { this.id = id; }
  char[] toString() {
    return "{" ~ std.string.toString(id) ~ "}";
  }
  uint toHash() { return id * 9; }
}
...

その場合、Object のメソッドを override しているのか、ObjectInterface を実装しているのか解らなくなるが、override 属性をつけてもコンパイルは通る。


こうしてみると、D の Object のメソッドって、print を除けば暗黙に呼ばれるものばかりだな。