메서드는 특별한 리시버 인자를 가지는 함수예요.
리시버는 func 키워드와 메서드 이름 사이에 있는 별도의 인자 목록으로 나타나요.
func (receiver type) MethodName(parameters) (returnTypes) {
}
리시버의 타입이 메서드와 같은 패키지에 정의되어 있을 때만 그 리시버를 가지는 메서드를 정의할 수 있어요.
package person
type Person struct {
Name string
}
func (p Person) Greetings() string {
return fmt.Sprintf("Welcome %s!", p.Name)
}
구조체의 메서드는 점 표기법으로 호출할 수 있어요.
p := Person{Name: "Bronson"}
fmt.Println(p.Greetings())
// Output: Welcome Bronson!
Person 인스턴스 p에서 Greetings() 메서드를 호출한 방식에 주목해 봐요.
객체 지향 프로그래밍 언어에서 메서드를 호출하는 방식과 똑같아요.
기억해 둬요. 메서드는 리시버 인자를 가진 함수일 뿐이에요. 메서드는 이름 충돌을 피하는 데 도움이 돼요. 메서드는 특정 리시버 타입에 묶여 있기 때문에 서로 다른 타입에 같은 메서드 이름을 쓸 수 있거든요.
import "math"
type rect struct {
width, height int
}
func (r rect) area() int {
return r.width * r.height
}
type circle struct {
radius int
}
func (c circle) area() float64 {
return math.Pow(float64(c.radius), 2) * math.Pi
}
리시버에는 값 리시버와 포인터 리시버, 두 가지 종류가 있어요.
지금까지 살펴본 메서드는 모두 값 리시버를 사용해요. 값 리시버는 메서드에 전달된 값의 복사본을 받아요. 그래서 메서드 안에서 리시버를 수정해도 호출한 쪽에서는 그 변경을 볼 수 없어요.
리시버가 가리키는 값을 수정하려면 포인터 리시버를 사용해 메서드를 선언하면 돼요.
타입 이름 앞에 *를 붙이면 돼요.
예를 들어 rect 타입이라면 포인터 리시버는 *rect로 선언해요.
이런 수정은 메서드를 호출한 쪽에서도 확인할 수 있어요.
type rect struct {
width, height int
}
func (r *rect) squareIt() {
r.height = r.width
}
r := rect{width: 10, height: 20}
fmt.Printf("Width: %d, Height: %d\n", r.width, r.height)
// Output: Width: 10, Height: 20
r.squareIt()
fmt.Printf("Width: %d, Height: %d\n", r.width, r.height)
// Output: Width: 10, Height: 10
참고: 이 연습 문제는 need-for-speed 연습 문제의 이어지는 내용이에요.
이 연습 문제에서는 여러 종류의 원격 조종 자동차끼리 경주를 진행해요. 각 자동차는 저마다의 속도와 배터리 소모 특성을 가지고 있어요.
자동차는 완충된(100%) 배터리로 시작해요. 리모컨으로 자동차를 운전할 때마다 자동차는 자신의 속도만큼 미터 단위로 이동하고, 남은 배터리 비율은 배터리 소모량만큼 줄어들어요.
자동차의 배터리 잔량이 배터리 소모량보다 낮으면 더 이상 자동차를 운전할 수 없어요.
원격 조종 자동차에는 두 가지 정보를 보여 주는 멋진 LED 디스플레이가 있어요.
"Driven <METERS> meters"."Battery at <PERCENTAGE>%".경주 트랙마다 고유한 거리가 있어요. 자동차는 배터리가 떨어지지 않고 트랙을 완주할 수 있는지 확인하는 방식으로 테스트해요.
자동차의 속도에 따라 주행한 미터 수를 갱신하고, 배터리 소모량에 따라 배터리를 줄이는 Drive 메서드를 Car에 구현하세요:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
car.Drive()
// car is now Car{speed: 5, batteryDrain: 2, battery: 98, distance: 5}
참고: 자동차의 배터리가 0 미만이 되는 상황이라면 자동차를 운전하려고 하면 안 돼요.
LED 디스플레이에 표시되는 대로 거리를 string으로 반환하는 DisplayDistance 메서드를 Car에 구현하세요:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
fmt.Println(car.DisplayDistance())
// Output: "Driven 0 meters"
LED 디스플레이에 표시되는 대로 배터리 비율을 string으로 반환하는 DisplayBattery 메서드를 Car에 구현하세요:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
fmt.Println(car.DisplayBattery())
// Output: "Battery at 100%"
경주를 완주하려면 자동차가 경주의 거리를 주행할 수 있어야 해요.
즉, 결승선을 통과하기 전에 배터리가 다 닳지 않아야 하죠.
trackDistance int를 매개변수로 받아 자동차가 경주를 완주할 수 있으면 true를, 그렇지 않으면 false를 반환하는 CanFinish 메서드를 구현하세요:
speed := 5
batteryDrain := 2
car := NewCar(speed, batteryDrain)
trackDistance := 100
car.CanFinish(trackDistance)
// => true
Exercism에 가입하고 Go 트랙을 개념 34개연습 문제 166개, 그리고 실제 사람의 멘토링과 함께 배우고 익혀 보세요. 모두 무료예요.