О назначении интерфейса - PullRequest
       2

О назначении интерфейса

0 голосов
/ 25 сентября 2019

Почему Go не думает, что это ошибка несоответствия типов, когда указатель структуры назначается интерфейсу?

package main

import "fmt"

type ABC interface {
    a() string
    b() int
}

type XYZ struct {
    aa string
    bb int
}

func (xyz XYZ) a() string {
    return "XYZ"
}

func (xyz XYZ) b() int {
    return 123
}

func main() {
    var xyz *XYZ
    var abc ABC = xyz // type of abc is *main.XYZ,I think that Golang can find this error here, but why not?

    fmt.Printf("%T\n", abc)

    a, ret := abc.(*XYZ)
    fmt.Println(a, ret) // type of a is *main.XYZ

    fmt.Println(a.a()) // will occur a error, because the type of a(*main.XYZ) not implements the interface ABC
}

Я хочу знать, почему Go не думает, что это ошибка "var abc ABC= xyz "

Ответы [ 3 ]

1 голос
/ 25 сентября 2019

XYZ осуществляет ABC.Это связано с тем, как определяются наборы методов (выделение добавлено):

Тип может иметь набор методов, связанный с ним.Набор методов типа интерфейса является его интерфейсом.Набор методов любого другого типа T состоит из всех методов, объявленных с типом получателя T. Набор методов соответствующего типа указателя * T является набором всех методов, объявленных с получателем * T или T (то есть, он также содержит набор методов T).

Набор методов определяет, реализован ли интерфейс :

Тип интерфейса определяетНабор методов называется его интерфейсом.Переменная типа интерфейса может хранить значение любого типа с набором методов, который является любым надмножеством интерфейса.Говорят, что такой тип реализует интерфейс .

При вызове *XYZ.a() компилятор Go всегда может автоматически разыменовать указатель для получения получателя значения.В этом нет недостатка, потому что получатель не может быть изменен (насколько это касается вызывающего абонента).

Обратное значение истинно тогда и только тогда, когда значение адресуемое:

type T struct {}
func (*T) M()

func main() {
    var t T
    t.M() // ok; t is addressable and the compiler rewrites this to (*t).M()

    var m map[string]T
    m["x"].M() // error: cannot take the address of m["x"]
}

См. Также: Наборы методов Голанга (указатель на получатель значения)

0 голосов
/ 26 сентября 2019

@ icza, возможно, я не понимаю всего, что вы сказали, но я думаю, что могу дать ответ на вопрос сейчас.когда я вызываю метод для значения (не указатель, метод структуры, который реализует интерфейс, и получатель метода является указателем).Golang создаст новый объект и скопирует значение из исходного значения структуры, затем iface.data укажет новый объект, теперь, когда мы передадим указатель нового объекта в метод, его можно будет изменить, но этоОперация не изменит значение структуры происхождения, это бесполезно, поэтому при назначении значения структуры интерфейсу (получателю указателя) Golang возникнет ошибка

0 голосов
/ 25 сентября 2019

произойдет ошибка, поскольку тип (* main.XYZ) не реализует интерфейс ABC

Неверно.*main.XYZ реализует ABC (иначе abc = xyz потерпит неудачу во время компиляции, попробуйте переименовать метод, например, b в c), но переменная a содержит указатель nil (типа*XYZ).А так как метод XYZ.a() имеет получатель значения, для вызова этого нужно будет разыменовать значение указателя типа *XYZ.Но указатель nil указывает на ничто, его нельзя разыменовать, попытка сделать это приводит к панике во время выполнения, как вы уже испытали.

Если вы инициализируете xyz в начале с не- nil указатель, он будет работать:

var xyz *XYZ = new(XYZ)

Попробуйте на Go Playground .

Также обратите внимание, что если XYZ.a() и XYZ.b() будетесли у вас есть указатель, то он также будет работать, если xyz равен nil:

func (xyz *XYZ) a() string {
    return "XYZ"
}

func (xyz *XYZ) b() int {
    return 123
}

func main() {
    var xyz *XYZ
    // ...

Попробуйте на Go Playground .Причина этого в том, что если получатели являются указателями, указатель nil не нужно разыменовывать для вызова методов с указателями получателей, поэтому паника во время выполнения не возникает.Конечно, если в методах вы бы ссылались на поля XZY.aa или XYZ.bb, это было бы паникой во время выполнения, но ваша текущая реализация методов не делает этого, поэтому она работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...