수신자와 인터페이스
값 수신자를 가진 메서드는 값과 포인터를 사용하여 호출할 수 있습니다.
포인터 수신자를 가진 메서드는 포인터 또는 주소 가능한 값을 사용하여 호출할 수 있습니다.
예를 들어,
type S struct {
data string
}
func (s S) Read() string {
return s.data
}
func (s *S) Write(str string) {
s.data = str
}
sVals := map[int]S{1: {"A"}}
// 값만을 사용하여 Read를 호출할 수 있습니다
sVals[1].Read()
// 다음은 컴파일되지 않습니다:
// sVals[1].Write("test")
sPtrs := map[int]*S{1: {"A"}}
// Read와 Write 메서드 모두 포인터를 사용하여 호출할 수 있습니다
sPtrs[1].Read()
sPtrs[1].Write("test")
마찬가지로, 메서드가 값을 수신자를 가지고 있더라도 포인터 수신자를 사용하여 인터페이스를 충족시킬 수 있습니다.
type F interface {
f()
}
type S1 struct{}
func (s S1) f() {}
type S2 struct{}
func (s *S2) f() {}
s1Val := S1{}
s1Ptr := &S1{}
s2Val := S2{}
s2Ptr := &S2{}
var i F
i = s1Val
i = s1Ptr
i = s2Ptr
// 다음 코드는 s2Val이 값이며 S2의 f 메서드가 값을 수신자로 가지지 않기 때문에 컴파일되지 않습니다
// i = s2Val
Effective Go에는 포인터 대 값에 대한 훌륭한 설명이 있습니다.
추가 사항:
- 유형은 값 수신자를 가진 메서드 집합과 포인터 수신자를 가진 메서드 집합을 가질 수 있습니다.
- 값 수신자를 가진 메서드 집합은 포인터 수신자를 가진 메서드 집합의 하위 집합이지만 그 역은 성립하지 않습니다.
- 규칙
- 값 객체는 값 수신자를 가진 메서드 집합만 사용할 수 있습니다.
- 포인터 객체는 값 수신자를 가진 메서드 집합과 포인터 수신자를 가진 메서드 집합을 모두 사용할 수 있습니다.
- 인터페이스 일치 (또는 구현)
- 유형은 인터페이스의 모든 메서드를 구현하면 해당 인터페이스와 일치합니다.
- 구체적으로, 유형의 값 메서드 집합이 인터페이스와 일치하거나 포인터 메서드 집합이 인터페이스와 일치합니다.
두 가지 특정 일치 유형이 있습니다:
- 값 메서드 집합이 인터페이스와 일치
- 값 또는 포인터 객체를 인터페이스 변수에 할당하는 것은 괜찮습니다. 왜냐하면 둘 다 값을 메서드 집합을 포함하기 때문입니다.
- 포인터 메서드 집합이 인터페이스와 일치
- 포인터 객체만 인터페이스 변수에 할당될 수 있으며 포인터 메서드 집합만 인터페이스와 일치하기 때문입니다.
- 값 객체가 인터페이스 변수에 할당되면 컴파일 중에 인터페이스 생존성 검사 메커니즘이 트리거됩니다.
s2Val에 대한 i = s2Val이 오류를 트리거하는 이유는 값 메서드 집합과 인터페이스가 일치하지 않기 때문입니다.