Вы не можете сделать это, потому что значения интерфейса не делают этого.
Что делают значения интерфейса - независимо от самого типа интерфейса; не имеет значения, является ли тип интерфейса пустым или нет - заключается в том, что они содержат две вещи:
- конкретный тип некоторого значения (или никакого типа); и
- значение этого конкретного типа (или никакого значения).
Так, если какая-то переменная 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
имеет некоторое набранное значение, и тип может содержать ноль указатели, значение часть 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))
}
(попробуйте это на Go детской площадке ).
Обычно это неправильный способ использования 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
.)