Вы не можете этого сделать, потому что значения интерфейса этого не делают.
Что делают значения интерфейса - независимо от самого типа интерфейса; не имеет значения, является ли тип интерфейса пустым или нет - они содержат две вещи:
- конкретный тип некоторого значения (или отсутствие типа); а также
- значение этого конкретного типа (или отсутствие значения).
Итак, если какая-то переменная v
или выражение e
имеет тип I
, где I
- это тип интерфейса, то вы можете с помощью некоторого синтаксиса проверить одно или оба из этих двух «полей». Это не struct
поля, поэтому вы не можете просто использовать v.type
, но вы можете сделать следующее:
switch v.(type) {
case int: // the value in v has type int
case *float64: // the value in v has type float64
// etc
}
.(type)
в switch
означает позвольте мне взглянуть на поле типа.
Получить фактическое значение сложнее, потому что Go более или менее требует, чтобы вы сначала проверили тип. В вашем случае вы знаете, что i
содержит либо Dog
, либо Cat
, поэтому вы можете написать:
var name string
switch i.(type) {
case Dog: name = i.(Dog).name
case Cat: name = i.(Cat).name
default: panic("whatever 'i' is, it is not a Dog or Cat")
}
fmt.Println(name)
Это довольно неуклюже, и есть много способов сделать его менее неуклюжим, но это всегда первый шаг: выяснить, что это за тип.
Что ж, иногда есть шаг перед первым шагом: выяснить, есть ли в переменной что-нибудь вообще. Вы делаете это с помощью:
if i == nil {
...
}
Однако обратите внимание, что если i
имеет какое-то типизированное значение и тип может содержать нулевые указатели, часть value i
может быть нулевой, а i == nil
будет ложью . Это потому, что в i
есть тип.
var i interface{}
var p *int
if i == nil {
fmt.Println("i is initially nil")
}
if p == nil {
fmt.Println("p is nil")
}
i = p
if i != nil {
fmt.Printf("i is now not nil, even though i.(*int) is %v\n", i.(*int))
}
(попробуйте это на игровой площадке).
Обычно это неправильный способ использования interface
Чаще всего - есть исключения - мы даже не пытаемся посмотреть на тип какого-либо интерфейса. Вместо этого мы определяем интерфейс, который предоставляет методы - функции, которые мы можем вызывать, - которые делают то, что нам нужно. См. ответ Бурака Сердара, в котором тип интерфейса имеет getName
метод. Затем, вместо того, чтобы пытаться выяснить, какой из ограниченного набора типов нам кто-то дал, мы просто говорим:
name := i.getName()
для вызова getName
метода для базового конкретного значения. Если i
содержит Dog
, это вызывает func (Dog) getName() string
, который вам необходимо определить. Если i
содержит Cat
, он вызывает func (Cat) getName() string
. Если вы решите добавить в свою коллекцию тип с именем Bird
, вы можете определить func (Bird) getName() string
и так далее.
(Обычно методы также экспортируются: GetName
, а не getName
.)
person
torek
schedule
29.01.2020