Go 인터페이스(interface)
인터페이스(interface)
인터페이스(interface)는 다형성(Polymorphism)을 구현하기 위한 기능이다.
아래의 예에서는 구조체 Person
도 구조체 Book
도 ToString()
이라는 메소드와 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"]
와 같이 참조할 수 없는 것은 아쉽다.