더북(TheBook)

4.2.2 메서드

메서드는 사용자 정의 타입 값에 호출할 수 있는 특별한 함수이다. 리시버 타입 변수에 메서드를 호출하면 변수가 메서드 내부로 전달되고, 전달된 변수는 메서드 내부에서 사용할 수 있다.

 

리시버 값 전달 방식

함수와 마찬가지로 메서드는 값에 의한 호출이 기본 방식이다. 메서드를 호출하면 리시버 변수의 값이 복사되어 메서드 내부로 전달되고, 메서드 내부에서는 리시버 변수의 값을 변경할 수 없다. 메서드 내부에서 리시버 변수의 값을 변경하려면 리시버 변수의 메모리 주소를 전달해야 한다.

참조에 의한 호출로 리시버 변수의 주소를 전달하려면 리시버 타입에 * 를 사용하여 포인터로 지정해야 한다. 이렇게 리시버를 포인터로 지정하면 메서드 호출 시 리시버 변수의 메모리 주소가 전달되므로 메서드 내부에서 리시버 변수의 값을 변경할 수 있다.


type quantity int
 
func (q quantity) greaterThan(i int) bool { return int(q) > i }
 
func (q *quantity) increment() { *q++ }
 
func (q *quantity) decrement() { *q-- }
 
func main() {
    q := quantity(3)
    q.increment()
    fmt.Printf("Is q(%d) greater than %d? %t \n", q, 3, q.greaterThan(3))
    q.decrement()
    fmt.Printf("Is q(%d) greater than %d? %t \n", q, 3, q.greaterThan(3))
}

실행 결과

Is q(4) greater than 3? true

Is q(3) greater than 3? false

여기서 increment()decrement() 메서드는 리시버를 포인터로 지정했으므로 메서드 내부에서 리시버 변수의 값을 변경할 수 있다.

메서드 내부에서 리시버 변수의 값을 변경해야 할 때만 리시버를 포인터로 지정하고, 그 외에는 보통 리시버 변수를 값 형태로 넘긴다. 하지만 예외 상황도 있다. 메서드 내부에서 리시버 변수의 값을 변경하지 않더라도 리시버 값의 크기가 크면 리시버를 포인터로 지정하기도 한다.

앞서 설명했듯이 값에 의한 호출은 리시버 변수의 값이 그대로 복사되어 메서드 내부로 전달된다. 만약 리시버 타입이 필드를 많이 갖는 구조체라면 리시버 변수의 값이 메서드 내부로 복사되는 과정에서 시스템 리소스가 많이 사용될 것이다. 리시버를 포인터로 지정하면 리시버 변수의 주소 값만 복사되므로 리소스를 절약할 수 있다.

실제 Go의 기본 라이브러리나 외부 패키지의 소스를 보면 리시버 변수의 값을 변경하지 않아도 리시버를 포인터로 지정하는 경우가 많다.

신간 소식 구독하기
뉴스레터에 가입하시고 이메일로 신간 소식을 받아 보세요.