IT박스

구조체 조각! = 구현하는 인터페이스 조각?

itboxs 2021. 1. 7. 07:47
반응형

구조체 조각! = 구현하는 인터페이스 조각?


Modelstruct에 의해 구현 된 인터페이스 Person있습니다.

모델 인스턴스를 얻기 위해 다음과 같은 도우미 함수가 있습니다.

func newModel(c string) Model {
    switch c {
    case "person":
        return newPerson()
    }
    return nil
}

func newPerson() *Person {
    return &Person{}
}

위의 접근 방식을 사용하면 적절한 형식의 Person 인스턴스를 반환 할 수 있습니다 (나중에 동일한 접근 방식으로 쉽게 새 모델을 추가 할 수 있음).

모델 조각을 반환하기 위해 비슷한 작업을 시도 할 때 오류가 발생합니다. 암호:

func newModels(c string) []Model {
    switch c {
    case "person":
        return newPersons()
    }
    return nil
}

func newPersons() *[]Person {
    var models []Person
    return &models
}

Go는 다음과 같이 불평합니다. cannot use newPersons() (type []Person) as type []Model in return argument

내 목표는 요청 어떤 모델 유형의 슬라이스를 반환하는 것입니다 (여부 []Person, []FutureModel, []Terminator2000, w / E). 내가 무엇을 놓치고 있으며 어떻게 이러한 솔루션을 올바르게 구현할 수 있습니까?


이것은 방금 답변 한 질문과 매우 유사합니다. https://stackoverflow.com/a/12990540/727643

짧은 대답은 당신이 맞다는 것입니다. 구조체 조각은 구조체가 구현하는 인터페이스 조각과 동일하지 않습니다.

A []Person와 a []Model는 메모리 레이아웃이 다릅니다. 이는 슬라이스 유형이 메모리 레이아웃이 다르기 때문입니다. A Model는 메모리에서 크기가 두 단어임을 의미하는 인터페이스 값입니다. 유형 정보에 대한 한 단어, 데이터에 대한 다른 단어. A Person는 크기가 포함 된 필드에 따라 달라지는 구조체입니다. A로부터 변환하기 위해 []PersonA를 []Model, 당신은 배열을 통해 루프가 필요하고 각 요소에 대한 형식 변환을 할 것입니다.

이 변환은 O (n) 작업이고 새 슬라이스가 생성되는 결과를 가져 오므로 Go는이를 암시 적으로 거부합니다. 다음 코드를 사용하여 명시 적으로 수행 할 수 있습니다.

models := make([]Model, len(persons))
for i, v := range persons {
    models[i] = Model(v)
}
return models

그리고 dskinner가 지적했듯이 슬라이스에 대한 포인터가 아닌 포인터 슬라이스를 원할 가능성이 큽니다. 슬라이스에 대한 포인터는 일반적으로 필요하지 않습니다.

*[]Person        // pointer to slice
[]*Person        // slice of pointers

아마도 이것은 반환 유형의 문제 *[]Person일 수 있습니다. 실제로 []*Person슬라이스의 각 인덱스가에 대한 참조 Person이고 슬라이스 []자체가 배열에 대한 참조임을 참조해야합니다.

다음 예를 확인하십시오.

package main

import (
    "fmt"
)

type Model interface {
    Name() string
}

type Person struct {}

func (p *Person) Name() string {
    return "Me"
}

func NewPersons() (models []*Person) {
    return models
}

func main() {
    var p Model
    p = new(Person)
    fmt.Println(p.Name())

    arr := NewPersons()
    arr = append(arr, new(Person))
    fmt.Println(arr[0].Name())
}

Stephen이 이미 질문에 답했고 당신은 초보자이기 때문에 조언을 제공하는 것을 강조합니다.

go의 인터페이스로 작업하는 더 좋은 방법은 Java와 같은 다른 언어에서 익숙한 것처럼 인터페이스를 반환하는 생성자를 가지지 않고 인터페이스를 암시 적으로 구현하기 때문에 각 객체에 대해 독립적으로 생성자를 갖는 것입니다.

대신에

newModel(type string) Model { ... }

당신은해야합니다

newPerson() *Person { ... }
newPolitician() *Politician { ... }

with Person and Politician both implementing the methods of Model. You can still use Person or Politician everywhere where a Model is accepted, but you can also implement other interfaces.

With your method you would be limited to Model until you do a manual conversion to another interface type.

Suppose I have a Person which implements the method Walk() and a Model implements ShowOff(), the following would not work straight forward:

newModel("person").ShowOff()
newModel("person").Walk() // Does not compile, Model has no method Walk

However this would:

newPerson().ShowOff()
newPerson().Walk()

Types T and []T are distinct types and distinct are their methods as well, even when satisfying the same interface. IOW, every type satisfying Model must implement all of the Model's methods by itself - the method receiver can be only one specific type.


As others have already answered, []T is a distinct type. I'd just like to add that a simple utility can be used to convert them generically.

import "reflect"

// Convert a slice or array of a specific type to array of interface{}
func ToIntf(s interface{}) []interface{} {
    v := reflect.ValueOf(s)
    // There is no need to check, we want to panic if it's not slice or array
    intf := make([]interface{}, v.Len())
    for i := 0; i < v.Len(); i++ {
        intf[i] = v.Index(i).Interface()
    }
    return intf
}

Now, you can use it like this:

ToIntf([]int{1,2,3})

Even if Go's implementation allowed this, it's unfortunately unsound: You can't assign a []Person to a variable of type []Model because a []Model has different capabilities. For example, suppose we also have Animal which implements Model:

var people []Person = ...
var models []Model = people // not allowed in real Go
models[0] = Animal{..} // ???
var person Person = people[0] // !!!

If we allow line 2, then line 3 should also work because models can perfectly well store an Animal. And line 4 should still work because people stores Persons. But then we end up with a variable of type Person holding an Animal!

Java actually allows the equivalent of line 2, and it's widely considered a mistake. (The error is caught at run time; line 3 would throw an ArrayStoreException.)

ReferenceURL : https://stackoverflow.com/questions/12994679/slice-of-struct-slice-of-interface-it-implements

반응형