인터페이스 사용하기


이재홍 http://www.pyrasis.com 2014.12.17 ~ 2015.02.07




인터페이스는 메서드 집합입니다. 단 인터페이스는 메서드 자체를 구현하지는 않습니다.

  • type 인터페이스명 interface { }
package main

import "fmt"

type hello interface { // 인터페이스 정의
}

func main() {
	var h hello    // 인터페이스 선언
	fmt.Println(h) // <nil>: 빈 인터페이스이므로 nil이 출력됨
}

실행 결과

nil

인터페이스를 선언하는 방법은 변수를 선언하는 방법과 같습니다. 즉, var 변수명 인터페이스 형식으로 선언합니다. 그리고 인터페이스는 다른 자료형과 동일하게 함수의 매개변수, 리턴값, 구조체의 필드로 사용할 수 있습니다.

여기서는 빈 인터페이스를 정의하고 선언해보았습니다. 빈 인터페이스기 때문에 할 수 있는 것이 없으며 fmt.Println으로 출력해봐도 nil이 나옵니다.

이제 메서드를 가지는 인터페이스를 정의해보겠습니다.

  • type 인터페이스명 interface { 메서드 }
type 인터페이스명 interface {
	메서드1() 리턴값_자료형
	메서드2()                  // 리턴값이 없을 때
}

{ } (중괄호) 블록안에 메서드 이름, 매개변수 자료형, 리턴값 자료형을 지정하여 한 줄 씩 나열합니다. 여기서는 , (콤마)로 구분하지 않습니다.

다음은 int 자료형에 메서드를 연결하고, 인터페이스로 해당 메서드를 호출합니다.

package main

import "fmt"

type MyInt int // int형을 MyInt로 정의

func (i MyInt) Print() { // MyInt에 Print 메서드를 연결
	fmt.Println(i)
}

type Printer interface { // Print 메서드를 가지는 인터페이스 정의
	Print()
}

func main() {
	var i MyInt = 5

	var p Printer // 인터페이스 선언

	p = i     // i를 인터페이스 p에 대입
	p.Print() // 5: 인터페이스를 통하여 MyInt의 Print 메서드 호출
}

먼저 type 새_자료형 자료형 형식으로 기존 자료형을 새 자료형으로 정의할 수 있습니다. 여기서는 기본 자료형인 int에 메서드를 연결하기 위해 MyInt를 새로 정의했습니다. 그 다음 MyIntPrint 메서드를 연결하고, 현재 변수의 값을 출력하도록 구현하였습니다.

이제 인터페이스를 정의합니다. 보통 인터페이스의 이름은 ~er 형태로 짓습니다(예: Reader, Writer). 우리는 Print 함수를 가지는 Printer 인터페이스를 정의했습니다.

type Printer interface {
	Print()
}

main 함수 안에서 인터페이스를 선언한 뒤 변수를 대입합니다. 그리고 인터페이스에 . (점)을 사용하여 메서드를 호출합니다. 여기서는 p.Print()처럼 인터페이스의 Print 메서드를 호출했지만 실제로는 MyIntPrint 메서드가 호출됩니다. 즉 인터페이스에 담긴 실제 타입(자료형, 구조체)의 메서드가 호출됩니다.

이제 인터페이스를 제대로 활용하기 위해 각기 다른 자료형 두 개를 인터페이스 한 개에 담아보겠습니다. 다음은 int 자료형과 사각형 구조체의 내용을 출력하고, int 자료형과 사각형 구조체의 인스턴스를 담을 수 있는 인터페이스를 정의한 예제입니다.

package main

import "fmt"

type MyInt int // int 형을 MyInt로 정의

func (i MyInt) Print() { // MyInt에 Print 메서드를 연결
	fmt.Println(i)
}

type Rectangle struct { // 사각형 구조체 정의
	width, height int
}

func (r Rectangle) Print() { // Rectangle에 Print 메서드를 연결
	fmt.Println(r.width, r.height)
}

type Printer interface { // Print 메서드를 가지는 인터페이스 정의
	Print()
}

func main() {
	var i MyInt = 5
	r := Rectangle{10, 20}

	var p Printer // 인터페이스 선언

	p = i     // i를 인터페이스 p에 대입
	p.Print() // 5: 인터페이스 p를 통하여 MyInt의 Print 메서드 호출

	p = r     // r을 인터페이스 p에 대입
	p.Print() // 10 20: 인터페이스 p를 통하여 Rectangle의 Print 메서드 호출
}

MyInt를 정의하고, 너비와 높이를 가지는 사각형 구조체 Rectangle을 정의했습니다. 그리고 MyInt, Rectangle 모두 자신의 내용을 출력하는 Print 함수를 구현했습니다. 이제 두 타입 모두 똑같은 Print 함수를 가지고 있습니다(여기서 똑같은 함수라는 것은 함수 이름, 매개변수 자료형, 리턴값 자료형이 모두 같은 상태를 뜻합니다).

MyInt 자료형, Ractangle 구조체, Printer 인터페이스를 그림으로 표현하면 다음과 같습니다.


그림 32-1 Printer 인터페이스

이제 인터페이스를 사용해보겠습니다.

func main() {
	var i MyInt = 5
	r := Rectangle{10, 20}

	var p Printer // 인터페이스 선언

	p = i     // i를 인터페이스 p에 대입
	p.Print() // 5: 인터페이스 p를 통하여 MyInt의 Print 메서드 호출

	p = r     // r을 인터페이스 p에 대입
	p.Print() // 10 20: 인터페이스 p를 통하여 Rectangle의 Print 메서드 호출
}

Printer 인터페이스 pMyInt 형 변수 i를 대입했습니다. 마찬가지로 다시 pRectangle 인스턴스를 대입했습니다. 이렇게 전혀 다른 타입을 인터페이스에 대입할 수 있습니다. 즉 인터페이스는 자료형이든 구조체든 타입에 상관없이 메서드 집합만 같으면 동일한 타입으로 봅니다.

인터페이스의 메서드를 호출하면 인터페이스에 담긴 실제 타입의 메서드가 호출되므로 MyInt 형 변수를 대입한 뒤 Print 함수를 호출하면 5가 출력되고, Rectangle 인스턴스를 대입하면 10과 20이 출력됩니다.

인터페이스를 선언하면서 초기화하려면 다음과 같이 :=를 사용하면 됩니다. 인터페이스에는 ( ) (괄호)를 사용하여 변수나 인스턴스를 넣어줍니다.

var i MyInt = 5
r := Rectangle{10, 20}

p1 := Printer(i) // 인터페이스를 선언하면서 i로 초기화
p1.Print()       // 5

p2 := Printer(r) // 인터페이스를 선언하면서 r로 초기화
p2.Print()       // 10 20

다음과 같이 배열(슬라이스) 형태로도 인터페이스를 초기화 할 수 있습니다.

var i MyInt = 5
r := Rectangle{10, 20}

pArr := []Printer{i, r} // 슬라이스 형태로 인터페이스 초기화
for index, _ := range pArr {
	pArr[index].Print() // 슬라이스를 순회하면서 Print 메서드 호출
}

for _, value := range pArr {
	value.Print() // 슬라이스를 순회하면서 Print 메서드 호출
}

저작권 안내

이 웹사이트에 게시된 모든 글의 무단 복제 및 도용을 금지합니다.
  • 블로그, 게시판 등에 퍼가는 것을 금지합니다.
  • 비공개 포스트에 퍼가는 것을 금지합니다.
  • 글 내용, 그림을 발췌 및 요약하는 것을 금지합니다.
  • 링크 및 SNS 공유는 허용합니다.

Published

01 June 2015