인터페이스

인터페이스 (Interfaces)

클래스가 특정 계약(contract)을 충족하도록 강제하는 D의 인터페이스 기능을 다룹니다. 인터페이스의 기본 규칙, 메서드 구현, 재구현, 메서드 계약, 그리고 COM·C++ 인터페이스처럼 특수한 변형까지 정리했어요.

출처: Interfaces — D Language Specification

본문

인터페이스에서 상속받은 클래스는 그 인터페이스 타입으로 암시적으로 변환될 수 있습니다.

인터페이스는 클래스에서 파생될 수 없고 오직 다른 인터페이스에서만 파생됩니다. 인터페이스 인스턴스는 상속된 어떤 인터페이스로든 암시적으로 변환됩니다. 클래스는 인터페이스를 여러 번 파생할 수 없습니다.

interface I
{
 void foo();
}

class A : I, I // error, duplicate interface
{
}

인터페이스의 인스턴스는 직접 생성할 수 없습니다:

interface I
{
 void foo();
}

...

I iface = new I(); // error, cannot create instance of interface

인터페이스 인스턴스는 Object로 캐스트하거나, 다른 클래스 타입으로 동적 캐스트할 수 있습니다.

인터페이스 메서드 본문 (Interface Method Bodies)

가상(virtual) 인터페이스 멤버 함수는 구현을 갖지 않습니다. 인터페이스는 static이나 final 함수를 구현할 수 있어요.

interface I
{
 void bar() { } // error, implementation not allowed
 static void foo() { } // ok
 final void abc() { } // ok
}

인터페이스는 멤버에 함수 템플릿을 가질 수 있습니다. 인스턴스화된 모든 함수는 암시적으로 final입니다.

interface I
{
 void foo(T)() { } // ok, it's implicitly final
}

인터페이스에서 상속받은 클래스는 final이거나 static인 인터페이스 멤버 함수를 오버라이드할 수 없습니다.

interface I
{
 void bar();
 static void foo() { }
 final void abc() { }
}

class C : I
{
 void bar() { } // ok
 void foo() { } // error, cannot override static I.foo()
 void abc() { } // error, cannot override final I.abc()
}

인터페이스 구현 (Implementing Interfaces)

인터페이스에서 상속받은 클래스에서 모든 가상 인터페이스 함수를 정의해야 합니다:

interface I
{
 void foo();
}

class A : I
{
 void foo() { } // ok, provides implementation
}

class B : I
{
 int foo() { } // error, no `void foo()` implementation
}

인터페이스는 기반 클래스에서 상속될 수 있고, 인터페이스 함수도 오버라이드될 수 있습니다:

interface I
{
 int foo();
}

class A : I
{
 int foo() { return 1; }
}

class B : A
{
 override int foo() { return 2; }
}

B b = new B();
assert(b.foo() == 2);

I i = b; // ok since B inherits A's I implementation
assert(i.foo() == 2);

인터페이스 재구현 (Reimplementing Interfaces)

인터페이스는 파생 클래스에서 재구현될 수 있습니다:

interface I
{
 int foo();
}

class A : I
{
 int foo() { return 1; }
}

class B : A, I // reimplement I
{
 override int foo() { return 2; }
}

B b = new B();
assert(b.foo() == 2);
I i = b;
assert(i.foo() == 2);

A a = b;
I i2 = a;
assert(i2.foo() == 2); // i2 has A's virtual pointer for foo which points to B.foo

재구현된 인터페이스는 모든 인터페이스 함수를 구현해야 하며, 슈퍼클래스에서 상속받지 않습니다:

interface I
{
 int foo();
}

class A : I
{
 int foo() { return 1; }
}

class B : A, I
{
} // error, no foo() for interface I

인터페이스 메서드 계약 (Interface Method Contracts)

인터페이스 멤버 함수는 함수 본문이 없어도 계약(contract)을 가질 수 있습니다. 계약은 그 인터페이스 멤버 함수를 구현하는 어떤 클래스 멤버 함수에도 상속됩니다.

interface I
{
 int foo(int i)
 in { assert(i > 7); }
 out (result) { assert(result & 1); }

 void bar();
}

const와 immutable 인터페이스 (Const and Immutable Interfaces)

인터페이스가 constimmutable 저장 클래스를 가지면, 인터페이스의 모든 멤버가 const 또는 immutable입니다. 이 저장 클래스는 상속되지 않습니다.

COM 인터페이스 (COM Interfaces)

인터페이스의 변형으로 COM 인터페이스가 있습니다. COM 인터페이스는 Windows COM 객체에 직접 매핑되도록 설계되었어요. 어떤 COM 객체든 COM 인터페이스로 표현할 수 있고, COM 인터페이스를 가진 어떤 D 객체든 외부 COM 클라이언트가 사용할 수 있습니다.

COM 인터페이스는 인터페이스 core.sys.windows.com.IUnknown에서 파생된 것으로 정의됩니다. COM 인터페이스는 일반 D 인터페이스와 다음 점에서 다릅니다:

  • 인터페이스 core.sys.windows.com.IUnknown에서 파생된다.
  • destroy의 인자가 될 수 없다.
  • 참조를 둘러싼 클래스 객체로 업캐스트할 수 없고, 파생 인터페이스로 다운캐스트할 수도 없다.
  • 다른 COM 인터페이스로 변환하려면 표준 COM 방식으로 그 인터페이스에 대한 QueryInterface()를 구현한다.
  • COM 인터페이스에서 파생된 클래스는 COM 클래스입니다.
  • COM 클래스 멤버 함수의 기본 링키지는 extern(System)입니다.

참고: D 인터페이스나 클래스(IUnknown에서 상속받지 않는 것들)의 기반 클래스 메서드를 구현하거나 오버라이드할 때는 명시적으로 extern(D) 링키지를 표시하세요.

import core.sys.windows.windows;
import core.sys.windows.com;

interface IText
{
 void write();
}

abstract class Printer : IText
{
 void print() { }
}

class C : Printer, IUnknown
{
 // Implements the IText `write` class method.
 extern(D) void write() { }

 // Overrides the Printer `print` class method.
 extern(D) override void print() { }

 // Overrides the Object base class `toString` method.
 extern(D) override string toString() { return "Class C"; }

 // Methods of class implementing the IUnknown interface have
 // the extern(System) calling convention by default.
 HRESULT QueryInterface(const(IID)*, void**);
 uint AddRef();
 uint Release();
}

이는 opCmp, toHash 등 다른 Object 메서드에도 동일하게 적용됩니다.

COM vtbl[]의 첫 번째 멤버는 InterfaceInfo에 대한 포인터가 아니라 첫 번째 가상 함수 포인터입니다.

참고: Modern COM Programming in D 문서도 참고하세요.

C++ 인터페이스 (C++ Interfaces)

C++ 인터페이스는 C++ 링키지로 선언된 인터페이스입니다:

extern (C++) interface Ifoo
{
 void foo();
 void bar();
}

이는 다음 C++ 선언에 대응하도록 의도된 것입니다:

class Ifoo
{
 virtual void foo();
 virtual void bar();
};

C++ 인터페이스에서 파생된 어떤 인터페이스도 C++ 인터페이스입니다. C++ 인터페이스는 D 인터페이스와 다음 점에서 다릅니다:

  • destroy의 인자가 될 수 없다.
  • 참조를 둘러싼 클래스 객체로 업캐스트할 수 없고, 파생 인터페이스로 다운캐스트할 수도 없다.
  • 멤버 함수의 기본 호출 규약이 D 호출 규약이 아니라 C++ 호출 규약이다.
  • vtbl[]의 첫 번째 멤버가 Interface에 대한 포인터가 아니라 첫 번째 가상 함수 포인터이다.

더 알아보기

  • Classes — 클래스의 상속과 가상 함수 규칙.
  • Enums — 열거형 선언.