Go Langauge #4 methods - countingmars/countingmars.github.io GitHub Wiki

Methods

Go는 클래스가 없지만, 타입(type 키워드로 정의된 것)에 method를 추가할 수는 있다.
method는 receiver를 가지는 함수이다.

receiver는 func 키워드와 함수명 사이에 나온다.

func (v Vertex) add() { fmt.Println(v.Lat, v.Long) }

위 예제에서 Vertex 타입의 메서드를 정의했다.
func와 add() 사이의 (v Vertex) 가 receiver이다.

마치 클래스의 method 호출하듯이 사용할 수 있다.

v := Vertex{}
v.add()

다만 메서드는 같은 패키지에 정의된 타입에 대해서만 생성할 수 있다. 다른 패키지의 타입에 메서드를 추가할 수는 없다.

Pointer receivers

포인터 receiver를 사용함으로써 오브젝트를 공유할 수 있다. 만약 value receiver를 쓴다면 원본 오브젝트의 복제본이 method에 전달된다. 따라서 오브젝트가 공유되지 못한다. 만약 method를 호출해서 receiver의 값이 변경되기를 원한다면 pointer receiver를 사용하자.

type Adder struct {
    Sum int
}
func (a *Adder) add(v int) {
    a.Sum += v
}
func (a Adder) addToCopy(v int) {
    a.Sum += v
}
func main() {
    adder := Adder{}
    adder.add(1)
    adder.addToCopy(1)
}

Methods and pointer indirection

method 혹은 function에 파라미터를 pointer로 사용한다면 호출 하는 곳에서도 타입을 맞춰줘야 한다. 하지만 method의 경우 편의를 위해 receiver의 타입이 value receiver인 경우에는, 편의를 위해 pointer receiver와 value receiver 둘 다를 허용해준다.

func (v Vertex) add() { ... }
func main() {
    v := Vertex{}
    p := &v
    v.add() // it's ok
    p.add() // it's also ok, interpreted as (*p).add()
}

Choosing a value or pointer receiver

pointer receiver를 써야 하는 두 가지 이유.

  1. receiver의 값을 변경하고 싶을 때
  2. receiver의 복사 비용을 줄이고 싶을 때 (receiver가 거대한 객체라면 더욱 효과적)

일반적으로 특정 type에 대한 receiver는 모두 pointer receiver이거나 혹은 모두 value receiver이어야 한다. 왜 그런지는 뒤에서 좀더 살펴보자.

Interfaces

interface는 method signature의 집합이다.
그리고 struct는 interface의 관계는 모호한 편이다. 쉽게 말하면 struct가 interface를 구현하기 위한 키워드는 go에서 제공되지 않는다. 단지 특정 struct의 method와 interface의 method가 일치한다면 특정 struct는 interface로 취급될 수 있다.
예제가 이해가 빠를 듯 하다. 예제를 주석과 함께 읽어보자.

// Adder 인터페이스를 정의한다.
type Adder interface {
    Add() int // method signature이다.
}
type Pair {
    a, b int
} 
// Pair 타입이 Adder 인터페이스에 정의된 method인 Add()를 구현하고 있다.
func (pair Pair) Add() int {
    return pair.a + pair.b
}
func main() {
    var adder Adder
    adder = Pair {1, 2} // adder 인터페이스에 Pair 객체를 할당한다.
    fmt.Println(adder.Add())
}

Interface values

interface value는 (value, type)의 튜플로 간주할 수도 있다.
interface의 method가 호출되면 receiver로 value가 전달되고, type에서 정의된 method가 호출된다.

Interface values with nil underlying values

nil에 대한 메서드 호출은 보통 다른 언어에서는 null pointer exception을 발생시키지만, go에서는 nil receiver를 우아하게 제어하는 코드를 작성할 수 있다.

func (pair Pair) add() int {
    if (pair == nil) { return } 
    ...
}
var adder Adder
var pair Pair
adder = pair
adder.add()

위의 코드에서 interface value인 adder는 Pair 타입의 nil value의 참조하고 있음을 이해하자.

empty interface

empty interface는 method signauture를 하나도 가지지 않는 interface이다. 이런 interface는 모든 타입을 처리할 수 있다. 예를 들어 fmt.Println()은 empty interface를 여러개 받을 수 있는 메서드이다.