Go 인터페이스(interface)

Go Goto 문(goto)

인터페이스(interface)

인터페이스(interface)는 다형성(Polymorphism)을 구현하기 위한 기능이다.

아래의 예에서는 구조체 Person도 구조체 BookToString()이라는 메소드와 PrintOut()라는 메소드를 구현하고 있다.

package main

import "fmt"

type Person struct {
	name string
}

func (p Person) ToString() string {
	return p.name
}
func (p Person) PrintOut() {
	fmt.Println(p.ToString())
}

type Book struct {
	title string
}

func (b Book) ToString() string {
	return b.title
}
func (b Book) PrintOut() {
	fmt.Println(b.ToString())
}

func main() {
	a1 := Person{name: "devkuma"}
	a2 := Book{title: "Go 공부합시다."}
	a1.PrintOut()
	a2.PrintOut()
}

실행 결과:

devkuma
Go 공부합시다.

ToString() 은 내용이 다르기 때문에 어쩔 수 없지만, PrintOut()는 내용도 동일하므로, 인터페이스를 이용해 하나의 함수에 정리할 수 있다.

Printable라는 인터페이스는 ToString()라고 하는 메소드를 서포트하고 있는 구조체이면, 자동적으로 적용하는 것이 가능해진다.

package main

import "fmt"

type Printable interface {
	ToString() string
}

func PrintOut(p Printable) {
	fmt.Println(p.ToString())
}

type Person struct {
	name string
}

func (p Person) ToString() string {
	return p.name
}

type Book struct {
	title string
}

func (b Book) ToString() string {
	return b.title
}

func main() {
	a1 := Person{name: "devkuma"}
	a2 := Book{title: "Go 공부합시다."}
	PrintOut(a1)
	PrintOut(a2)
}

interface {}형

go 는 제네릭이 없다. 대신 interface{} 를 활용하여 비슷하게 구현 할 수 있다.

함수가 없는 인터페이스 interface {}any형과 같이 사용할 수 있다.

다음 함수는 모든 유형의 인수를 받을 수 있다.

func funcA(a interface {}) {
    // ...
}

.(유형)interface{} 형의 변수를 다른 형태로 변환한다.

func funcA(a interface{}) {
    fmt.Printf("%d\n", a.(int))
}

형식 변환의 두 번째 반환 값은 형식 변환 가능 여부를 반환한다. 객체가 interface { … } 로 정의한 인터페이스를 구현하고 있을지 어떨지를 조사할 수가 있다.

func PrintOut (a interface {}) {
     // a를 Printable 인터페이스를 구현 한 객체로 변환해 본다.
    q, ok := a.(Printable)
    if ok {
        // 변환 할 수 있으면 인터페이스를 호출한다.
        fmt.Println(q.ToString())
    } else {
        fmt.Println("Not printable.")
    }
}

switch 변수.(type) { ... }을 사용하여 유형을 결정할 수도 있다.

func funcA (a interface {}) {
    switch a.(type) {
    case bool:
        fmt.Printf("%t\n", a)
    case int:
        fmt.Printf("%d\n", a)
    case string:
        fmt.Printf("%s\n", a)
    }
}

interface {}any와 같이 사용할 수 있다는 특징을 살려, 임의의 형태의 값을 가지는 맵을 정의할 수도 있다.

p1 := map[string]interface{} {
    "name": "devkuma",
    "age": 23,
}

아래와 같이 하면 계층 구조를 가지는 Python의 dict와 같이 정의할 수 있다.

package main

import "fmt"

func main() {
	type any interface{}
	type dict map[string]any

	p1 := dict{
		"name": "devkuma",
		"age":  23,
		"address": dict{
			"zip": "01234",
			"tel": "012-3456-7890",
		},
	}

	name := p1["name"]
	tel := p1["address"].(dict)["tel"] // any를 dict로 변환한 후 참조

	fmt.Println(name)
	fmt.Println(tel)
}

실행 결과:

devkuma
012-3456-7890

p1["address"]["tel"]와 같이 참조할 수 없는 것은 아쉽다.




최종 수정 : 2023-03-26