英文:
What does "dynamic type" mean in a Go interface?
问题
“Go语言实战:深入解析Go编程”(Ivo Balbaert)一书中包含了这样一句话,我不太理解:
> 接口类型可以包含对实现该接口的任何类型实例的引用(接口具有所谓的动态类型)
请问这是什么意思,能给一个例子说明,并解释一下为什么这样做有用吗?
英文:
The Way to Go: A Thorough Introduction To The Go Programming Language (Ivo Balbaert) contains this sentence which I don't quite understand:
> An interface type can contain a reference to an instance of any of the types that implement the interface (an interface has what is called a dynamic type)
What is an example of this, and why this is useful?
答案1
得分: 12
假设你有一个接口:
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
。
换句话说:接口值的动态类型是最初转换为接口类型的值的类型。
英文:
Say you have an interface:
type I interface{ F() }
And two implementations of said interface:
type S struct{}
func (S) F() { }
type T struct{}
func (T) F() { }
Then:
var x I
x = S{}
Now the static type of x
is I
, and its dynamic type is S
.
You can reassign x
a value of a different type that implements I
:
x = T{}
Now the static type of x
is still I
(it will never change), and its dynamic type is T
.
IOW: the dynamic type of an interface value is the type of the value that was originally converted to an interface type.
答案2
得分: 4
定义
接口具有所谓的动态类型。
动态类型意味着它可以持有对不同类型(例如字符串、整数等)的引用,并且可以在运行时更改,而静态类型在编译时进行检查,不能更改。
然而,书中给出的定义受到了质疑。根据官方的Golang网站:
有人说Go的接口是动态类型,但这是误导。它们是静态类型:接口类型的变量始终具有相同的静态类型,即使在运行时存储在接口变量中的值可能会更改类型,该值仍将满足接口。
示例
尽管接口不是真正的动态类型,但以下是如何使用它们。
假设您有以下接口。
type Locker interface {
Lock()
Unlock()
}
实际上这是sync
包中的Locker
。
现在,如果您创建两个结构体来实现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() {
// ...
}
根据您给出的定义:
接口类型可以包含对实现接口的任何类型实例的引用(接口具有所谓的动态类型)
可以翻译为:
Locker(接口)类型可以包含对实现其合约的任何类型实例的引用(例如Foo、Bar等)。
在代码中表示为:
var lock Locker
lock = &Foo{"Foo"} // 将Foo类型的实例分配给Locker变量
lock.Lock() // 我们可以调用接口Locker定义的所有函数
lock.Unlock()
lock.String() // 这将不起作用,因为Locker接口没有定义String()函数,即使Foo实现了它。
lock = &Bar{}
lock.Lock()
在上面的示例中,我们可以看到变量lock
持有对不同类型的引用,但它并不是真正的动态类型,因为将类型分配给lock
的条件是其类型符合Locker合约。而这部分在编译时定义。
为什么有用?
这篇文章将比我更好地解释接口的用途。https://softwareengineering.stackexchange.com/questions/108240/why-are-interfaces-useful
英文:
Definition
> An interface has what is called a dynamic type
A dynamic type means that it can hold a reference to different types (e.g. string, int, ...) and that it can change at runtime, whereas a static type is checked at compile time and cannot change.
However, the definition given by the book is questioned. According to the official Golang website :
> Some people say that Go's interfaces are dynamically typed, but that is misleading. They are statically typed: a variable of interface type always has the same static type, and even though at run time the value stored in the interface variable may change type, that value will always satisfy the interface.
Example
Even though an interface is not truly a dynamic type, here is how to use them.
Say you have the following interface.
type Locker interface {
Lock()
Unlock()
}
That's actually the Locker
from sync
package.
Now if you create two structs that implement the functions defined by the Locker interface. In other words, if you fulfil the Locker
contract you will be able to use the structs Foo
and Bar
as a Locker
interface.
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() {
// ...
}
So given the definition you gave :
> An interface type can contain a reference to an instance of any of the types that implement the interface (an interface has what is called a dynamic type)
It could be translated into :
> A Locker (interface) type can contain a reference to an instance of any of the types that implement its contract (e.g. Foo, Bar, ...).
Which in code means :
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()
In the example above we can see that the variable lock
holds a reference to different types, but it is not truly dynamic because the condition to assign a type to lock
is that its type comply with the Locker
contract. And that part is defined at compile time.
Why is it useful?
This post will explain why interfaces are useful better than me. https://softwareengineering.stackexchange.com/questions/108240/why-are-interfaces-useful
答案3
得分: 0
每个变量都有一个类型。这个类型可以是静态类型(int、string、bool、map、struct、slice等),也可以是接口类型。
接口可以由任何静态类型实现(通常是别名类型)。
接口类型的变量实际上存储在两个部分中。第一部分是底层静态类型的符号名称。第二部分是以该静态类型格式存储的数据。
因此,如果一个变量声明为接口类型,这意味着它的类型是动态的,因为底层类型可以是实现该接口的任何静态类型。
通常,这种模式的有用性在于从某些共享行为中定义一类类型的扩展行为。通过这样做,您可以将关心的功能封装在任何特定情况下,而不必担心给定类型的具体细节。例如,任何Reader都可以从中读取,一旦您确定您有一个reader,您就不需要担心底层类型可能具有的所有其他方法,这意味着您可以轻松地定义一个使用任何 reader的单个函数。
这与多态性接近,并共享大部分好处。
英文:
Every variable has a type. That type is either an a static type (int, string, bool, map, struct, slice, etc) or an interface type.
An interface can be implemented by any static type (typically by an aliased type).
A variable of an interface type is actually stored in two parts. The first part is the symbolic name of the underlying static type. The second part is the data in the format of that static type.
So if a variable is declared to be of an interface type, that means that its type is dynamic in the sense that the underlying type could end up being any of the static types that implement the interface.
Typically the usefulness of this pattern is to define an extended behavior of a class of types from some shared behaviour. Doing this you gain the ability to encapsulate the functionality that you care about in any particular case, rather than worrying about the specifics of a given type. For example, any Reader can be Read from, and once you've decided that you have a reader you don't need to worry about all the other methods that underlying type might have, meaning that you can easily define a single function that uses any reader.
This is close to polymorphism and shares most of the benefits.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论