1. Interface


Go 에서는 interface도 일종의 타입으로 아래와 같이 type으로 선언한다.

type DuckInterface interface {
	Fly()
	Walk(distance int) int
}

그리고 이 3가지 조건을 만족해야 한다.

  • Method 명이 필수
  • 매개변수와 반환이 다르더라도 이름이 같은 메서드는 존재할 수 없다.
  • 인터페이스에서는 메서드 구현을 포함하지 않는다.

2. Duck Typing


만약 어떤 새를 봤는데, 그 새가 오리처럼 걷고 오리처럼 날고 오리처럼 소리내면 나는 그 새를 오리라고 부르겠다.

Go 언어는 interface을 구현할 때, 명시적으로 하는 것이 아닌. 해당 인터페이스가 정의한 모든 메서드를 구현한 구조체는 interface로 사용될 수 있다.

type Stringer interface{
	String() string
}

type Student struct {
}

func (s *Student) String() string {
}

// Student 는 Stringer interface을 구현(implement) 하고 있다.

3. 포함된 인터페이스


type Reader interface {
	Read() (n int, err error)
	Close() error
}

type Writer interface {
	Write() (n int, err error)
	Close() error
}

// Read(), Writer(), Close() Method을 가지게 됨
type ReadWriter interface {
	Reader // Reader의 메서드 집합을 포함
	Writer // Writer의 메서드 집합을 포함
}

원래는 이름이 같은 함수가 있으면 안되지만, 두 인터페이스의 Close의 메서드 형식(시그니처)가 완전히 동일하므로 합쳐질 수 있다.

4. 빈 인터페이스


빈 인터페이스는 어떤 함수도 정의하지 않은 인터페이스.

따라서 빈 인터페이스는 어떤 함수, 메서드, 변수값이든 받을 수 있다.

// interface{} replaced to any
func PrintVal(v interface{}) {
	switch t := v.(type) {
	// Type switch 문법. interface 의 타입을 알고 싶을 때 사용. Switch 에서만 사용 가능
	// v.(type) 으로 사용 가능
	case int:
		fmt.Printf("v is int %d\n", int(t))
	case float64:
		fmt.Printf("v is float64 %f\n", float64(t))
	case string:
		fmt.Printf("v is string %s\n", string(t))
	default:
		fmt.Printf("Not supported type: %T:%v\n", t, t)
	}
}

type Student struct {
	Age int
}

func main() {
	PrintVal(10)
	PrintVal(3.14)
	PrintVal("Hello")
	PrintVal(Student{15})
}

5. 인터페이스 기본 값


package main

type Attacker interface {
	Attack()
}

func main() {
	var att Attacker // 기본값은 nil
	att.Attack()
}
// 실행하면 invalid memory address or nil pointer dereference

6. 변환하기


인터페이스 변수를 타입 변화를 통해 구체화된 다른 타입이나 다른 인터페이스로 변환할 수 있다.

구체화된 다른 타입으로 변환

var a Interface
t := a.(ConcreteType)

// Example
type Student struct {
	Age int
}

func (s *Student) String() string {
	return fmt.Sprintf("Student Age:%d", s.Age)
}

func PrintAge(stringer Stringer) {
	s := stringer.(*Student) // *Student type으로 변환
	fmt.Printf("Age: %d\n", s.Age)
}

당연하겠지만, 인터페이스를 구체화된 타입으로 변환하려면, 해당 타입이 인터페이스 메서드 집합을 포함하고 있어야 한다.

type Stringer interface {
	String() string
}

type Student struct {
}

func main() {
	var stringer Stringer
	stringer.(*Student) // Student not implemented Stringer
}

또한 변환하려는 타입이 인터페이스를 포함(인터페이스가 구현하는 모든 메소드를 구현) 하였다하더라도, 실제 인터페이스 변수가 가르키는 인스턴스가 변환하려는 타입이 아닌 경우 런타임에러가 발생한다.

(표현이 어렵지만, 인터페이스 I을 구현하는 구체화된 타입 A, B가 있을 때, A의 인스턴스 a를 인터페이스 I에 넣고 B로 변환하려 하면 런타임 에러가 발생한다는 뜻)


type Stringer interface {
	String() string
}

type Student struct {
}

func (s *Student) String() string {
	return "Student"
}

type Actor struct {
}

func (a *Actor) String() string {
	return "Actor"
}

func ConvertType(stringer Stringer) {
	student := stringer.(*Student)
	fmt.Println(student)
}

func main() {
	actor := &Actor{}
	ConvertType(actor)
}

// 아래 처럼 에러 발생
~/W/NightWatch/B/G/T/chapter18/ex18.9 2026.04.03 !4 ?2 ──────  system 22:09:28
❯ ./ex18.9 
panic: interface conversion: main.Stringer is *main.Actor, not *main.Student

goroutine 1 [running]:
main.ConvertType({0x4cfa28?, 0x5a5c40?})
        /home/wonjaek36/Workspaces/NightWatch/Book/Golang/Tuckers-Go-Programming/chapter18/ex18.9/ex18.9.go:24 +0x6f
main.main()
        /home/wonjaek36/Workspaces/NightWatch/Book/Golang/Tuckers-Go-Programming/chapter18/ex18.9/ex18.9.go:30 +0x25

다른 인터페이스로 변환하기

var a AInterface = ConcreteType {}
b := a.(BInterface)

타입을 변환할 때, 런타임 에러가 아닌 성공 여부를 반환 받아서 에러 핸들링을 할 수 있다.

var a Interface
t, ok := a.(ConcreteType)
c, ok := reader.(Closer)
if ok {
	// When type changed successfully...
}
if c, ok := reader.(Closer); ok {
	// In a line...
}