답변:
이 컴파일 타임 오류는 구체적 유형을 인터페이스 유형 에 지정하거나 전달 (또는 변환)하려고 할 때 발생 합니다. 타입 자체는 인터페이스를 구현하지 않고 type에 대한 포인터 만 구현합니다 .
예를 보자.
type Stringer interface {
String() string
}
type MyType struct {
value string
}
func (m *MyType) String() string { return m.value }
Stringer
인터페이스 유형은 하나의 방법 만이 있습니다 String()
. 인터페이스 값에 저장된 모든 값 Stringer
에는이 방법이 있어야합니다. 또한을 만들고 포인터 수신기를 사용 MyType
하여 메서드 MyType.String()
를 만들었습니다 . 이는 메소드가 유형 의 메소드 세트 에 있지만 의 메소드 세트 에는 없음을 의미 합니다.String()
*MyType
MyType
MyType
type의 변수에 값을 할당하려고 Stringer
하면 해당 오류가 발생합니다.
m := MyType{value: "something"}
var s Stringer
s = m // cannot use m (type MyType) as type Stringer in assignment:
// MyType does not implement Stringer (String method has pointer receiver)
우리는 유형의 값을 할당하려고한다면 모든 것이 괜찮 *MyType
로를 Stringer
:
s = &m
fmt.Println(s)
그리고 우리는 예상 결과를 얻습니다 ( Go Playground 에서 시도하십시오 ).
something
따라서이 컴파일 타임 오류를 얻는 데 필요한 요구 사항은 다음과 같습니다.
문제를 해결할 수있는 가능성 :
구조체와 embedding을 사용할 때 , 종종 인터페이스를 구현하는 것은 "사용자"가 아니라 (메소드 구현을 제공함),에 포함 된 타입 struct
입니다. 이 예에서와 같이 :
type MyType2 struct {
MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: m}
var s Stringer
s = m2 // Compile-time error again
다시 한 번, 컴파일 시간 오류는의 메소드 세트에 embed MyType2
의 String()
메소드를 포함하지 않기 때문에의 메소드 세트 MyType
만 포함 *MyType2
하므로 다음 작업이 수행됩니다 ( Go Playground 에서 시도 ).
var s Stringer
s = &m2
포인터*MyType
가 아닌 포인터 를 포함 하고 사용하는 경우 MyType2
에도 Go Playground 에서 시도해보십시오 .
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = m2
또한 우리가 포함하거나 ( MyType
또는 *MyType
) 포인터를 사용 *MyType2
하면 항상 작동합니다 ( Go Playground 에서 시도하십시오 ).
type MyType2 struct {
*MyType
}
m := MyType{value: "something"}
m2 := MyType2{MyType: &m}
var s Stringer
s = &m2
스펙의 관련 섹션 (섹션 유형에서 ) :
구조체 유형
S
과이라는 유형이 주어지면T
다음과 같이 승격 된 메소드가 구조체의 메소드 세트에 포함됩니다.
S
익명 필드가 포함 된 경우T
메소드 세트S
와*S
둘 다 수신자와 함께 승격 된 메소드를 포함T
합니다. 의 메소드 세트*S
에는 수신자와 함께 승격 된 메소드도 포함됩니다*T
.- 만약이
S
익명의 필드 포함*T
, 방법 세트를S
하고*S
모두 수신기 방법 승진 포함T
또는*T
.
다시 말해, 비 포인터 타입을 임베드하면 비 포인터 임 베더의 메소드 세트는 비 포인터 리시버가있는 메소드 만 임베드 된 유형에서 가져옵니다.
포인터 유형을 포함 시키면 비 포인터 임 베더의 메소드 세트는 포인터 및 비 포인터 수신자를 모두 포함하는 메소드를 포함합니다 (임베디드 유형에서).
임베디드 타입이 포인터인지 아닌지에 관계없이 임 베더에 포인터 값을 사용하는 경우, 임 베더에 대한 포인터의 메소드 세트는 항상 임베디드 타입의 포인터와 비 포인터 수신자 모두를 가진 메소드를 얻는다.
노트 :
매우 유사한 경우가 있습니다. 즉, 값을 래핑하는 인터페이스 값이 MyType
있고 다른 인터페이스 값 을 입력 하려고하면 Stringer
. 이 경우 어설 션은 위에서 설명한 이유로 유지되지 않지만 약간 다른 런타임 오류가 발생합니다.
m := MyType{value: "something"}
var i interface{} = m
fmt.Println(i.(Stringer))
런타임 패닉 ( Go Playground 에서 시도 ) :
panic: interface conversion: main.MyType is not main.Stringer:
missing method String
assert 타입 대신 변환하려고하면, 우리가 이야기하고있는 컴파일 타임 에러가 발생합니다 :
m := MyType{value: "something"}
fmt.Println(Stringer(m))
MyType
변경할 수없는 경우 인터페이스 {}로 값을 래핑하여 "참고 :"의 끝에 언급 된 문제를 실제로 어떻게 해결합니까? MyType
나는 이것과 같은 것을 시도했지만 (&i).(*Stringer)
작동하지 않았다. 가능합니까?
x := i.(MyType)
수 있습니다. 예를 들어 , 포인터 리시버가있는 메소드를 호출 할 수 있습니다 (예 : 변수의 주소를 지정할 수 있기 때문에 성공 i.String()
하는 약칭) (&i).String()
. 그러나 값 (지정된 값)을 변경하는 포인터 방법은 인터페이스 값에 래핑 된 값에 반영되지 않으므로 이치에 맞지 않습니다.
*T
가 포함 되지 않으며, 종종 사본 만 존재 / 수신되므로 주소를 가져 오는 것이 허용되는 경우 메소드 포인터 수신기를 사용하면 사본 만 수정할 수 있습니다 (원본이 수정되었다고 가정하면 혼란). 리플렉션 SetString 사용 예제는이 답변을 참조하십시오 . S
S
짧게 유지하려면이 코드가 있고이 인터페이스를 구현하는 Loader 인터페이스와 WebLoader가 있다고 가정하십시오.
package main
import "fmt"
// Loader defines a content loader
type Loader interface {
Load(src string) string
}
// WebLoader is a web content loader
type WebLoader struct{}
// Load loads the content of a page
func (w *WebLoader) Load(src string) string {
return fmt.Sprintf("I loaded this page %s", src)
}
func main() {
webLoader := WebLoader{}
loadContent(webLoader)
}
func loadContent(loader Loader) {
loader.Load("google.com")
}
따라서이 코드는이 컴파일 시간 오류를 줄 것입니다
./main.go:20:13 : loadContent의 인수에서 webLoader (유형 WebLoader)를 Loader 유형으로 사용할 수 없음 : WebLoader가 Loader를 구현하지 않음 (Load 메소드에 포인터 수신자가 있음)
따라서 당신이해야 할 일은 webLoader := WebLoader{}
다음 으로 변경 하는 것입니다.
webLoader := &WebLoader{}
func (w *WebLoader) Load
포인터 리시버를 받아들이도록 이 함수 를 정의하기 때문에 왜 수정 될까요? 자세한 설명은 @icza 및 @karora 답변을 참조하십시오
이런 종류의 일이 발생하는 것을 본 또 다른 경우는 일부 메소드가 내부 값을 수정하고 다른 메소드는 수정하지 않는 인터페이스를 작성하려는 경우입니다.
type GetterSetter interface {
GetVal() int
SetVal(x int) int
}
이 인터페이스를 구현하는 것은 다음과 같습니다.
type MyTypeA struct {
a int
}
func (m MyTypeA) GetVal() int {
return a
}
func (m *MyTypeA) SetVal(newVal int) int {
int oldVal = m.a
m.a = newVal
return oldVal
}
구현 유형에는 포인터 수신기 인 메소드와 그렇지 않은 메소드가있을 수 있으며 GetterSetters와 같은 다양한 다양한 기능을 가지고 있기 때문에 테스트에서 모두 예상대로 수행하고 싶습니다.
내가 이런 식으로해야한다면 :
myTypeInstance := MyType{ 7 }
... maybe some code doing other stuff ...
var f interface{} = myTypeInstance
_, ok := f.(GetterSetter)
if !ok {
t.Fail()
}
그런 다음 위에서 언급 한 "X는 Y를 구현하지 않습니다 (Z 메서드에는 포인터 수신기가 있습니다)"오류 (컴파일 타임 오류 이므로) 내 테스트가 실패한 이유를 정확하게 파악 하지 못하는 날 이 있습니다. .
대신 포인터를 사용하여 유형 검사를 수행해야합니다.
var f interface{} = new(&MyTypeA)
...
또는:
myTypeInstance := MyType{ 7 }
var f interface{} = &myTypeInstance
...
그런 다음 모든 테스트에 만족합니다!
하지만 기다려! 내 코드에는 어딘가에 GetterSetter를 허용하는 메소드가 있습니다.
func SomeStuff(g GetterSetter, x int) int {
if x > 10 {
return g.GetVal() + 1
}
return g.GetVal()
}
다른 유형의 메소드에서 이러한 메소드를 호출하면 오류가 발생합니다.
func (m MyTypeA) OtherThing(x int) {
SomeStuff(m, x)
}
다음 통화 중 하나가 작동합니다.
func (m *MyTypeA) OtherThing(x int) {
SomeStuff(m, x)
}
func (m MyTypeA) OtherThing(x int) {
SomeStuff(&m, x)
}
func (m *MyType)
") 또는 none 이어야한다는 것 입니다. 그렇습니까?func (m *MyType)
& 같은 다른 유형의 "멤버 함수"를 혼합 할 수 있습니까func (m MyType)
?