인터페이스(F#)
인터페이스는 다른 클래스에서 구현하는 관련 멤버로 이루어진 집합을 지정합니다.
// Interface declaration:
[ attributes ]
type interface-name =
[ interface ]
[ inherit base-interface-name ...]
abstract member1 : [ argument-types1 -> ] return-type1
abstract member2 : [ argument-types2 -> ] return-type2
...
[ end ]
// Implementing, inside a class type definition:
interface interface-name with
member self-identifier.member1 argument-list = method-body1
member self-identifier.member2 argument-list = method-body2
// Implementing, by using an object expression:
[ attributes ]
let class-name (argument-list) =
{ new interface-name with
member self-identifier.member1 argument-list = method-body1
member self-identifier.member2 argument-list = method-body2
[ base-interface-definitions ]
}
member-list
설명
인터페이스 선언은 어떠한 멤버도 구현되지 않는다는 점을 제외하고는 클래스 선언과 비슷합니다. 인터페이스 선언에서는 키워드 abstract에서 알 수 있듯이 모든 멤버가 추상입니다. 추상 메서드에 대해서는 메서드 본문을 제공하지 않습니다. 그러나 default 키워드와 함께 별도의 멤버 정의를 메서드로 포함하여 기본 구현을 제공할 수도 있습니다. 이 방법은 다른 .NET 언어에서 기본 클래스에 가상 메서드를 만드는 것과 같습니다. 이와 같은 가상 메서드는 인터페이스를 구현하는 클래스에서 재정의할 수 있습니다.
인터페이스를 구현하는 데는 개체 식을 사용하거나 클래스 형식을 사용하는 두 가지 방법이 있습니다. 어떤 경우든 클래스 형식이나 개체 식을 통해 인터페이스의 추상 메서드에 대한 메서드 본문을 제공합니다. 구체적인 구현 방식은 인터페이스를 구현하는 각 형식에 따라 다릅니다. 따라서 형식이 다르면 인터페이스 메서드도 서로 다를 수 있습니다.
간단한 구문을 사용하는 경우에는 정의의 시작과 끝을 표시하는 interface 및 end 키워드를 생략할 수 있습니다. 이러한 키워드를 사용하지 않으면 컴파일러에서 어떤 구문이 사용되는지 분석하여 형식이 클래스인지 인터페이스인지를 유추합니다. 멤버를 정의하거나 다른 클래스 구문을 사용한 경우에는 형식이 클래스인 것으로 해석됩니다.
.NET 코드 작성 스타일에 따르면 인터페이스는 모두 대문자 I로 시작해야 합니다.
클래스 형식을 사용한 인터페이스 구현
다음 코드에서와 같이 interface 키워드, 인터페이스 이름 및 with 키워드를 사용하고 그 뒤에 인터페이스 멤버 정의를 추가하여 클래스 형식으로 하나 이상의 인터페이스를 구현할 수 있습니다.
type IPrintable =
abstract member Print : unit -> unit
type SomeClass1(x: int, y: float) =
interface IPrintable with
member this.Print() = printfn "%d %f" x y
인터페이스 구현은 상속되므로 파생 클래스에서는 이를 구현할 필요가 없습니다.
인터페이스 메서드 호출
인터페이스 메서드는 인터페이스를 통해서만 호출할 수 있으며 인터페이스를 구현하는 형식의 개체를 통해서는 호출할 수 없습니다. 따라서 이러한 메서드를 호출하기 위해서는 :> 연산자나 upcast 연산자를 사용하여 인터페이스 형식으로 업캐스팅해야 할 수도 있습니다.
개체가 SomeClass 형식일 때 인터페이스 메서드를 호출하려면 다음 코드에서와 같이 개체를 인터페이스 형식으로 업캐스팅해야 합니다.
let x1 = new SomeClass1(1, 2.0)
(x1 :> IPrintable).Print()
또는 다음 예제에서와 같이 인터페이스 메서드를 업캐스팅하고 호출하는 메서드를 개체에 대해 선언할 수도 있습니다.
type SomeClass2(x: int, y: float) =
member this.Print() = (this :> IPrintable).Print()
interface IPrintable with
member this.Print() = printfn "%d %f" x y
let x2 = new SomeClass2(1, 2.0)
x2.Print()
개체 식을 사용한 인터페이스 구현
개체 식을 사용하면 인터페이스를 간단히 구현할 수 있습니다. 개체 식은 명명된 형식을 만들 필요가 없고 추가 메서드 없이 인터페이스 메서드를 지원하는 개체만 있으면 되는 경우에 유용합니다. 다음 코드에서는 개체 식의 예를 보여 줍니다.
let makePrintable(x: int, y: float) =
{ new IPrintable with
member this.Print() = printfn "%d %f" x y }
let x3 = makePrintable(1, 2.0)
x3.Print()
인터페이스 상속
인터페이스는 하나 이상의 기본 인터페이스에서 상속할 수 있습니다.
type Interface1 =
abstract member Method1 : int -> int
type Interface2 =
abstract member Method2 : int -> int
type Interface3 =
inherit Interface1
inherit Interface2
abstract member Method3 : int -> int
type MyClass() =
interface Interface3 with
member this.Method1(n) = 2 * n
member this.Method2(n) = n + 100
member this.Method3(n) = n / 10