[Go] 메소드 본문
Method
이전에 Java를 사용해본 사람이라며 Method라는 이름이 친숙하게 느껴질 것이다. 필자 또한 Java를 주로 사용했기 때문에 이번 파트를 공부하며 Method라는 명칭이 굉장히 반갑게 느껴졌다.
하지만 Go는 Java와 달리 클래스가 존재하지 않기 때문에 어떤 식으로 메소드를 사용하는지 의구심이 들었다.
Go에서의 메소드는 구조체 바깥에서 정의되며 리시버를 통해 구조체와 연결된다.
Receiver
메소드는 구조체 바깥에서 선언되기 때문에 메소드가 어떤 구조체에 속하는지 표시할 방법이 필요하다. 이를 위해 리시버를 사용하여 메소드가 속한 구조체를 알려준다.
🚀Method 선언
메소드를 선언하기 위해서는 리시버를 ()
로 명시해야 한다.
func (r Rabbit) info() int {
return r.width * r.height
}
위의 코드에서 (r Rabbit)
부분이 리시버다. 리시버를 통해 우리는 info()
메소드가 Rabbit
타입에 속해있다는 것을 알 수 있다. 구조체 변수로 선언된 r
은 메소드 내에서 매개변수처럼 이용된다.
모든 로컬 타입은 리시버 타입이 될 수 있으며, 별칭 타입 또한 리시버가 될 수 있다. int
타입 또한 별칭 타입을 만들면 메소드를 가질 수 있다.
package main
import "fmt"
type myInt int
func (a myInt) add(b int) int {
return int(a) + b
}
func main() {
var a myInt = 10
fmt.Println(a.add(30)) // 40
var b int = 20
fmt.Println(myInt(b).add(50)) // 70
}
포인터 메소드 vs 값 타입 메소드
package main
import "fmt"
type account struct {
balance int
firstName string
lastName string
}
func (a1 *account) withdrawPointer(amount int) {
a1.balance -= amount
}
func (a2 account) withdrawValue(amount int) {
a2.balance -= amount
}
func (a3 account) withdrawReturnValue(amount int) account {
a3.balance -= amoun
return a3
}
func main() {
var mainA *account = &account{ 100, "Joe", "Park" }
mainA.withdrawPointer(30)
fmt.Println(mainA.balance) //70
mainA.withdrawValue(20)
fmt.Println(mainA.balance) // 70
var mainB account = mainA.withdrawReturnValue(20)
fmt.Println(mainB.balance) // 50
mainB.withdrawPointer(30)
fmt.Println(mainB.balance) // 20
}
위의 코드에서 withdrawPointer()
메소드는 리시버로 포인터를 갖기 때문에 *account
타입에 속하고, withdrawValue()
, withdrawReturnValue()
메소드는 값 타입을 리시버로 갖기 때문에 account
에 속한다.
포인터 메소드를 호출하면 포인터가 가리키고 있는 메모리의 주소값이 복사되지만, 값 타입 메소드를 호출하면 리시버 타입의 모든 값이 복사된다.
withdrawPoint()
메소드가 호출되면 mainA
포인터 변수가 갖는 값인 메모리 주소가 복사되기 때문에 a1
과 mainA
는 동일한 인스턴스를 가리키게 된다. 그렇기 때문에 a1
의 balance
를 변경하게 되면 mainA
의 balance
또한 변경된다.
반면, withdrawValue()
메소드를 호출할 때는 값이 복사되어 전달되기 때문에 a2
와 mainA
는 서로 다른 메모리 주소를 갖게 된다.
그렇기 때문에 a2
의 balance
를 변경해도 mainA
의 balance
는 변경되지 않는다. 이를 해결하기 위해서 withdrawReturnValue()
메소드처럼 변경된 값을 다시 반환해야 한다.
withdrawReturnValue()
메소드에서는 값 복사가 호출 시와 결과값 반환 시 두 번 이루어진다. 이는 mainA
, a3
, mainB
가 각각 다른 메모리 주소를 갖게 된다는 것을 의미한다.
메소드 호출
mainA
는 포인터 변수이고, withdrawValue()
는 account
값 타입을 리시버로 받는 메소드다. 포인터인 mainA
는 바로 호출할 수 없고 (*main).withdrawValue(20)
과 같이 값 타입으로 변환하여 호출하여야 한다.
Go에서는 자동으로 mainA
의 값으로 변환하여 호출한다. 반대의 경우에도 동일한데, mainB.withdrawPointer(30)
에서 mainB
는 account
값 타입 변수이고 withdrawPointer()
는 *account
포인터를 리시버로 받는 메소드다.
동일하게 바로 호출이 불가능하기 때문에 주소 연산자를 사용해 (&mainB).withdrawPointer(30)
처럼 포인터로 변환 후 호출해야 하지만 Go에서는 자동으로 mainB
의 메모리 주소값으로 변환하여 호출한다.
'Dev' 카테고리의 다른 글
[Go] 함수 고급 (0) | 2024.12.02 |
---|---|
[Go] 인터페이스 (3) | 2024.11.10 |
[Go] 슬라이스 (0) | 2024.11.07 |
그림으로 이해하는 객체 지향 설계 5원칙 [SOLID] (2) | 2024.07.20 |
CPU-Scheduling (2) | 2024.04.07 |