Featured image of post Golang: 9. 인터페이스

Golang: 9. 인터페이스

구조체가 필드들의 집합체라면, interface는 메서드들의 집합체이다.

인터페이스는 타입(type)이 구현해야 하는 메서드 원형(prototype)들을 정의한다. 어떠한 사용자 정의 타입이 인터페이스를 구현하려면 선언한 인터페이스가 갖는 모든 메서드들을 구현하면 된다.

1
2
3
4
type Shape interface {
    area() float64
    perimeter() float64
}

인터페이스는 구조체와 마찬가지로 type문을 사용하여 정의한다.

인터페이스 구현

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//Rect 정의
type Rect struct {
    width, height float64
}
 
//Circle 정의
type Circle struct {
    radius float64
}
 
//Rect 타입에 대한 Shape 인터페이스 구현 
func (r Rect) area() float64 { return r.width * r.height }
func (r Rect) perimeter() float64 {
     return 2 * (r.width + r.height)
}
 
//Circle 타입에 대한 Shape 인터페이스 구현 
func (c Circle) area() float64 { 
    return math.Pi * c.radius * c.radius
}
func (c Circle) perimeter() float64 { 
    return 2 * math.Pi * c.radius
}

Shape 인터페이스를 구현하기 위해서는 area(), perimeter() 2개 메서드만 구현하면 된다.

인터페이스 사용

인터페이스를 사용하는 일반적인 예로 함수가 파라미터로 인터페이스를 받을 수 있다.

함수 파라미터가 인터페이스인 경우, 어떤 타입이든 해당 인터페이스를 구현하기만 하면 모두 입력 파라미터로 사용될 수 있다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
func main() {
    r := Rect{10., 20.}
    c := Circle{10}
 
    showArea(r, c)
}
 
func showArea(shapes ...Shape) {
    for _, s := range shapes {
        a := s.area() //인터페이스 메서드 호출
				p := s.perimeter()
        println(a, p)
    }
}

Rect 구조체와 Circle 구조체는 Shape 인터페이스에서 선언한 area, perimeter 메서드를 구현하고 있기 때문에 Shape 인터페이스를 상속했다고 볼 수 있다. 따라서 showArea에서 Shape 인터페이스 파라미터 입력으로 사용 가능하다.

인터페이스 타입

Go 프로그래밍을 하다보면 흔히 빈 인터페이스(empty interface)를 자주 접할 수 있는데, 이는 인터페이스 타입(interface type)으로 불린다.

여러 표준패키지들의 함수 Prototype을 살펴보면, 빈 인터페이스가 자주 등장한다.

  • 빈 인터페이스는 interface{} 로 표현한다.
1
2
3
func Marshal(v interface{}) ([]byte, error);
 
func Println(a ...interface{}) (n int, err error);

빈 인터페이스는 메서드를 전혀 갖지 않는 인터페이스이다.

Go의 모든 Type은 적어도 0개의 메서드를 구현하므로, Go에서 모든 Type을 의미한다.

즉, 빈 인터페이스는 어떠한 타입도 담을 수 있는 컨테이너로 볼 수 있고, 여러 다른 언어에서 흔히 일컫는 Dynamin Type으로 사용할 수 있다. (C#, java ⇒ object, C,C++ ⇒ void*)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main
 
import "fmt"
 
func main() {
    var x interface{}
    x = 1 
    x = "Tom"
 
    printIt(x)
}
 
func printIt(v interface{}) {
    fmt.Println(v) //Tom
}

Type Assertion

인터페이스 타입의 x와 타입 T에 대하여 x.(T)로 표현했을 때, 이는 x가 nil이 아니며, x는 T 타입에 속한다는 것을 확인한다. 이러한 표현방식을 Type Assertion 이라고 부른다.

만약 x가 nil 이거나 x의 타입이 T가 아니라면, 런타임 에러가 발생하고, x가 T 타입인경우는 T 타입 x를 반환한다.