为啥我可以键入别名函数并在不强制转换的情况下使用它们?

Posted

技术标签:

【中文标题】为啥我可以键入别名函数并在不强制转换的情况下使用它们?【英文标题】:Why can I type alias functions and use them without casting?为什么我可以键入别名函数并在不强制转换的情况下使用它们? 【发布时间】:2013-10-20 12:02:52 【问题描述】:

在 Go 中,如果你定义了一个新类型,例如:

type MyInt int

然后您不能将 MyInt 传递给期望 int 的函数,反之亦然:

func test(i MyInt) 
    //do something with i


func main() 
    anInt := 0
    test(anInt) //doesn't work, int is not of type MyInt

很好。但是为什么这同样不适用于函数呢?例如:

type MyFunc func(i int)
func (m MyFunc) Run(i int) 
    m(i)


func run(f MyFunc, i int) 
    f.Run(i)


func main() 
    var newfunc func(int) //explicit declaration
    newfunc = func(i int) 
        fmt.Println(i)
    
    run(newfunc, 10) //works just fine, even though types seem to differ

现在,我没有抱怨,因为它让我不必像在第一个示例中那样,将newfunc 显式转换为MyFunc;它似乎不一致。我确信这是有充分理由的。谁能赐教?

我问的原因主要是因为我想以这种方式缩短我的一些相当长的函数类型,但我想确保这样做是预期和可接受的:)

【问题讨论】:

type 在 Go 中比在 Scala 中更有用。 Scala only 有类型别名,唉。 Go 现在实际上有类型别名 github.com/golang/go/issues/18130 有人能解释一下第二个代码 sn-p 吗?我真的无法得到那些函数声明 【参考方案1】:

原来,这是我对 Go 如何处理类型的误解,可以通过阅读规范的相关部分来解决:

http://golang.org/ref/spec#Type_identity

我不知道的相关区别是 namedunnamed 类型。

命名类型是具有名称的类型,例如 int、int64、float、string、bool。此外,您使用 'type' 创建的任何类型都是命名类型。

未命名 类型是诸如 []string、map[string]string、[4]int 之类的类型。它们没有名称,只是与它们的结构相对应的描述。

如果您比较两个命名类型,则名称必须匹配才能使它们可互换。如果您比较命名类型和未命名类型,那么只要底层表示匹配,就可以了!

例如给定以下类型:

type MyInt int
type MyMap map[int]int
type MySlice []int
type MyFunc func(int)

以下内容无效:

var i int = 2
var i2 MyInt = 4
i = i2 //both named (int and MyInt) and names don't match, so invalid

以下是可以的:

is := make([]int)
m := make(map[int]int)
f := func(i int)

//OK: comparing named and unnamed type, and underlying representation
//is the same:
func doSlice(input MySlice)...
doSlice(is)

func doMap(input MyMap)...
doMap(m)

func doFunc(input MyFunc)...
doFunc(f)

我有点内疚,我没有早点知道,所以我希望能为其他人澄清一下类型云雀!并且意味着比我最初想象的要少得多:)

【讨论】:

也可以使用is := make(MySlice, 0); m := make(MyMap),在某些情况下更具可读性。【参考方案2】:

问题和答案都很有启发性。但是,我想提出一个在 lytnus 的回答中并不清楚的区别。

命名类型不同于未命名类型

命名类型的变量可分配给未命名类型的变量,反之亦然。

不同命名类型的变量不能相互分配。

http://play.golang.org/p/uaYHEnofT9

import (
    "fmt"
    "reflect"
)

type T1 []string
type T2 []string

func main() 
    foo0 := []string
    foo1 := T1
    foo2 := T2
    fmt.Println(reflect.TypeOf(foo0))
    fmt.Println(reflect.TypeOf(foo1))
    fmt.Println(reflect.TypeOf(foo2))

    // Output:
    // []string
    // main.T1
    // main.T2

    // foo0 can be assigned to foo1, vice versa
    foo1 = foo0
    foo0 = foo1

    // foo2 cannot be assigned to foo1
    // prog.go:28: cannot use foo2 (type T2) as type T1 in assignment
    // foo1 = foo2

【讨论】:

以上是关于为啥我可以键入别名函数并在不强制转换的情况下使用它们?的主要内容,如果未能解决你的问题,请参考以下文章

为啥切片函数在不明确使用 dplyr 的情况下不起作用

为啥c ++ std::hash会创建一个仿函数结构并且可以在不每次创建结构的情况下调用它

如何在不确认的情况下强制 cp 覆盖

是否可以在不强制转换的情况下初始化 float32 或 float16 的随机数组?

Jackson 如何在不强制转换的情况下将 JsonNode 转换为 ArrayNode?

如何在不向下转换的情况下调用派生类的成员函数