2015-10-26 4 views
4

путь: Тщательное Введение в язык программирования Go (Ivo Balbaert) содержит это предложение, которое я не совсем понимаю:Что означает «динамический тип» в интерфейсе Go?

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

Что является примером этого и почему это полезно?

+2

«Почему это полезно?» чувствует себя слишком широким вопросом. –

+1

https://golang.org/ref/spec#Variables: «Статический тип (или просто тип) переменной - это тип, указанный в его объявлении, тип, указанный в новом вызове или составном литерале, или тип элемент структурированной переменной. Переменные типа интерфейса также имеют отдельный динамический тип, который является конкретным типом значения, назначенного переменной во время выполнения (если значение не является предопределенным идентификатором nil, который не имеет типа). динамический тип может меняться во время исполнения, но значения, хранящиеся в переменных интерфейса, всегда присваиваются статическому типу переменной ». –

ответ

1

Определение

интерфейс имеет то, что называется динамическим типом

Динамический тип означает, что он может содержать ссылку на различные типы (например, строка, INT, ...) и что он может меняться во время выполнения, тогда как статический тип проверяется во время компиляции и не может измениться.

Однако определение, данное книгой, подвергается сомнению. Согласно официальному сайту Голанга:

Некоторые люди говорят, что интерфейсы Go являются динамически типизированными, но это вводит в заблуждение. Они статически типизированы: переменная типа интерфейса всегда имеет один и тот же статический тип, и хотя во время выполнения значение, хранящееся в переменной интерфейса, может изменять тип, это значение всегда будет удовлетворять интерфейсу.

Source

Пример

Даже если интерфейс не действительно динамический тип, вот как их использовать.

Скажите, что у вас есть следующий интерфейс.

type Locker interface { 
    Lock() 
    Unlock() 
} 

Это на самом деле в Locker из sync пакета.

Теперь, если вы создадите две структуры, реализующие функции, определенные интерфейсом Locker. Другими словами, если вы выполняете договор Locker, вы сможете использовать структуры Foo и Bar в качестве интерфейса Locker.

type Foo struct { 
    A string 
} 

func (f *Foo) String() string { 
    return f.A 
} 

func (f *Foo) Lock() { 
    // ... 
} 

func (f *Foo) Unlock() { 
    // ... 
} 

type Bar struct {} 

func (b *Bar) Lock() { 
    // ... 
} 

func (b *Bar) Unlock() { 
    // ... 
} 

Так дано определение вы дали:

тип интерфейса может содержать ссылку на экземпляр любого из типов, которые реализуют интерфейс (интерфейс имеет то, что называется динамический тип)

Это может быть переведено на:

шкафчике (интерфейс) станд e может содержать ссылку на экземпляр любого из типов, реализующих его контракт (например, Foo, Bar, ...).

Что в коде означает:

var lock Locker 

lock = &Foo{"Foo"} // We assign an instance of type Foo to a Locker var 
lock.Lock() // We can call all functions defined by the interface Locker 
lock.Unlock() 
lock.String() // This won't work because the Locker interface does not define the String() function, even though Foo implements it. 

lock = &Bar{} 
lock.Lock() 

В приведенном выше примере мы можем видеть, что переменная lock содержит ссылку на различные типы, но это не по-настоящему динамичным, так как условие для назначения типа для lock заключается в том, что его тип соответствует договору Locker. И эта часть определяется во время компиляции.

Почему это полезно?

Это сообщение объяснит, почему интерфейсы полезны лучше меня. https://softwareengineering.stackexchange.com/questions/108240/why-are-interfaces-useful

+0

Это не объясняет, что означает «динамический тип». – rightfold

+0

@Elyse Потому что это не то, что он просил. «Каков пример этого и почему это полезно?» – basgys

+0

Как это связано с «Тип интерфейса может содержать ссылку на экземпляр любого из типов, реализующих интерфейс»? Можете ли вы дать конкретные ссылки на эти "reference"/"type"/"instance"? –

2

Скажем, у вас есть интерфейс:

type I interface{ F() } 

И две реализации указанного интерфейса:

type S struct{} 
func (S) F() { } 

type T struct{} 
func (T) F() { } 

Тогда:

var x I 
x = S{} 

Теперь статический тип x является I, и его динамический тип - S.

Вы можете переназначить x значение другого типа, который реализует I:

x = T{} 

Теперь статический тип x еще I (она никогда не изменится), и его динамический тип T.

IOW: динамический тип значения интерфейса представляет собой тип значения, которое первоначально было преобразовано в тип интерфейса.

+0

Как это связано с «Тип интерфейса может содержать ссылку на экземпляр любого из типов, реализующих интерфейс»? Можете ли вы дать конкретные ссылки на эти "reference"/"type"/"instance"? –

+0

Я думаю, что формулировка автора оставляет желать лучшего. Это может быть более понятно: «Экземпляр типа интерфейса ** - это ссылка на экземпляр любого из типов, которые удовлетворяют интерфейсу». I.e, переменной 'x', объявленной статическим типом' I', может быть назначен любой тип, который удовлетворяет интерфейсу 'I'; когда он назначен, теперь он имеет динамический тип любого типа значения. – nfirvine

0

Каждая переменная имеет тип. Этот тип является либо статическим типом (int, string, bool, map, struct, slice и т. Д.), Либо типом интерфейса.

Интерфейс может быть реализован любым статическим типом (обычно с псевдонимом).

Переменная типа интерфейса фактически хранится в двух частях. Первая часть - это символическое имя базового статического типа. Вторая часть - это данные в формате этого статического типа.

Итак, если переменная объявлена ​​как тип интерфейса, это означает, что ее тип является динамическим в том смысле, что базовый тип может оказаться любым из статических типов, реализующих интерфейс.

Обычно полезность этого шаблона заключается в определении расширенного поведения класса типов из некоторого общего поведения. При этом вы получаете возможность инкапсулировать функциональность, которая вам нужна в любом конкретном случае, а не беспокоиться о специфике данного типа. Например, любой Reader может быть прочитан, и как только вы решили, что у вас есть читатель, вам не нужно беспокоиться обо всех других методах, которые может иметь базовый тип, что означает, что вы можете легко определить одну функцию, которая использует любой читатель.

Это близко к полиморфизму и имеет большую часть преимуществ.

Смежные вопросы