Go类型别名的类型切换

huangapple go评论89阅读模式
英文:

Go type switch for type aliases

问题

这个问题的答案可能很明显,但我还是问一下,因为我没有找到一个好的解释。

我有两个示例,它们做的事情几乎一样,但是第一个示例使用了int类型,而另一个示例使用了接口。

接口示例:https://play.golang.org/p/yb2oVaOJGF

type Apple interface{}
type Orange interface{}

type Basket struct {
	Fruit interface{}
}

func getFruites(basket Basket) {
	switch t := basket.Fruit.(type) {
	case Apple:
		apples, ok := t.(int)
		if !ok {
			log.Panic("Rotten apples!")
		}
		fmt.Println("Apples:", apples)
	case Orange:
		oranges, ok := t.(int)
		if !ok {
			log.Panic("Rotten oranges!")
		}
		fmt.Println("Oranges:", oranges)
	}
}

func main() {
	appleBasket := Basket{
		Fruit: Apple(10),
	}

	getFruites(appleBasket)

	orangeBasket := Basket{
		Fruit: Orange(10),
	}

	getFruites(orangeBasket)
}

int示例:https://play.golang.org/p/_z8Mm0II41

type Apple int
type Orange int

type Basket struct {
	Fruit interface{}
}

func getFruites(basket Basket) {
	switch t := basket.Fruit.(type) {
	case Apple:
		apples := t
		fmt.Println("Apples:", apples)
	case Orange:
		oranges := t
		fmt.Println("Oranges:", oranges)
	}
}

func main() {
	appleBasket := Basket{
		Fruit: Apple(10),
	}

	getFruites(appleBasket)

	orangeBasket := Basket{
		Fruit: Orange(10),
	}

	getFruites(orangeBasket)
}

有人能解释一下为什么它们产生不同的输出吗?

英文:

The answer to this is probably pretty obvious, but I'm asking anyways because I failed to find a good explanation for this.

I have two examples that I've made, they do pretty much the same thing, however, the first one uses int's and the other one is using interfaces:

Interface: https://play.golang.org/p/yb2oVaOJGF

type Apple interface{}
type Orange interface{}

type Basket struct {
	Fruit interface{}
}

func getFruites(basket Basket) {
	switch t := basket.Fruit.(type) {
	case Apple:
		apples, ok := t.(int)
		if !ok {
			log.Panic("Rotten apples!")
		}
		fmt.Println("Apples:", apples)
	case Orange:
		oranges, ok := t.(int)
		if !ok {
			log.Panic("Rotten oranges!")
		}
		fmt.Println("Oranges:", oranges)
	}
}

func main() {
	appleBasket := Basket{
		Fruit: Apple(10),
	}

	getFruites(appleBasket)

	orangeBasket := Basket{
		Fruit: Orange(10),
	}

	getFruites(orangeBasket)
}

Int: https://play.golang.org/p/_z8Mm0II41

type Apple int
type Orange int

type Basket struct {
	Fruit interface{}
}

func getFruites(basket Basket) {
	switch t := basket.Fruit.(type) {
	case Apple:
		apples := t
		fmt.Println("Apples:", apples)
	case Orange:
		oranges := t
		fmt.Println("Oranges:", oranges)
	}
}

func main() {
	appleBasket := Basket{
		Fruit: Apple(10),
	}

	getFruites(appleBasket)

	orangeBasket := Basket{
		Fruit: Orange(10),
	}

	getFruites(orangeBasket)
}

Could someone please explain why they produce different output?

答案1

得分: 1

在将Apple和Orange定义为interface{}的情况下,类型切换会被实现该接口的任何内容满足。

对于空接口来说,它可以是任何东西,并且会选择第一个匹配的情况。

当Apple和Orange是非接口类型(int)的“别名”时,只有显式声明为Apple或Orange的变量才能满足类型切换。

英文:

In the case where Apple and Orange are defined as interface{}, the type switch is satisfied by anything that implements that interface.

For the empty interface, that is anything at all, and it takes the first case that matches.

When Apple and Orange are "aliases" for a non-interface type (int), only a variable that is explicitly an Apple or Orange can satisfy the type switch.

答案2

得分: 1

因为你将Apple和Orange类型定义为空接口,所以它们可以被任何类型满足。

空接口本质上没有具体的类型限制。

在第一个示例中,一切都可以断言为int类型,无论是Apple还是Orange。

看一下你第一个示例代码的这个小改动。

package main

import (
	"fmt"
	"log"
)

type Apple interface{}
type Orange interface{}

type Basket struct {
	Fruit interface{}
}

func getFruits(basket Basket) {
	switch t := basket.Fruit.(type) {
	case int:
		fmt.Println("空接口满足了int类型")
	case Apple:
		apples, ok := t.(int)
		if !ok {
			log.Panic("烂苹果!")
		}
		fmt.Println("苹果数量:", apples)
	case Orange:
		oranges, ok := t.(int)
		if !ok {
			log.Panic("烂橙子!")
		}
		fmt.Println("橙子数量:", oranges)
	}
}

func main() {
	appleBasket := Basket{
		Fruit: Apple(10),
	}

	getFruits(appleBasket)

	orangeBasket := Basket{
		Fruit: Orange(10),
	}

	getFruits(orangeBasket)
}

链接:https://play.golang.org/p/LDtvbXnjT7

英文:

Because you are defining the Apple and Orange types to an empty interface they are satisfied by anything.

The empty interface is in essence nothing specific.

Everything in the first example can be asserted as the int type neither Apple or Oranges.

Look at this small change to your first example's code.

package main

import (
	"fmt"
	"log"
)

type Apple interface{}
type Orange interface{}

type Basket struct {
	Fruit interface{}
}

func getFruites(basket Basket) {
	switch t := basket.Fruit.(type) {
	case int: 
		fmt.Println("empty interfaces are satisfying an int")
	case Apple:
		apples, ok := t.(int)
		if !ok {
			log.Panic("Rotten apples!")
		}
		fmt.Println("Apples:", apples)
	case Orange:
		oranges, ok := t.(int)
		if !ok {
			log.Panic("Rotten oranges!")
		}
		fmt.Println("Oranges:", oranges)
	}
}

func main() {
	appleBasket := Basket{
		Fruit: Apple(10),
	}

	getFruites(appleBasket)

	orangeBasket := Basket{
		Fruit: Orange(10),
	}

	getFruites(orangeBasket)
}

https://play.golang.org/p/LDtvbXnjT7

答案3

得分: 0

由于你说其他答案不够直观,我尝试从完全不同的角度来解释。你提到的情况与你的类型别名无关,而与Go中接口的工作方式有关。为了更清楚地展示你提供的两个代码之间的区别,这里有一个完全不使用类型别名但输出类似于你的接口示例的不同代码。

Playground链接:https://play.golang.org/p/0tyDV28cp2b

package main

import (
	"fmt"
	"log"
)

// 创建两个具有完全相同签名的接口
// 为什么?这样实现其中一个接口的每个结构体都会自动实现另一个接口

type interface1 interface {
	Foo() int
}

type interface2 interface {
	Foo() int
}

// 为上面定义的接口创建一个实现
type impl int

func (i impl) Foo() int { return int(i) }

// 注意,Fruit的类型是"interface2"
// 尽管如此,它总是会进入类型开关语句中的"interface1"分支
// 这是因为任何实现interface2的东西也会实现interface1
type Basket struct {
	Fruit interface2
}

func getFruits(basket Basket) {
	switch t := basket.Fruit.(type) {
	case interface1:
		ans, ok := t.(impl)
		if !ok {
			log.Panic("Rotten ans!")
		}
		fmt.Println("Interface1:", ans)
	case interface2:
		ans, ok := t.(impl)
		if !ok {
			log.Panic("Rotten ans!")
		}
		fmt.Println("Interface2:", ans)
	}
}

func main() {
	appleBasket := Basket{
		Fruit: impl(10),
	}

	getFruits(appleBasket)

	orangeBasket := Basket{
		Fruit: impl(10),
	}

	getFruits(orangeBasket)
}

希望这次解释更加直观明了。

英文:

Since you said other answers didn't feel intuitive, I am attempting to explain it from a completely different standpoint. The case you mention has nothing to do with your type aliases and everything to do with how interfaces work in Go. To more clearly show the difference between the two codes you provided, here is a different code that gives the similar output as your interface example, but doesn't use type aliases at all.

Playground link: https://play.golang.org/p/0tyDV28cp2b

package main

import (
	"fmt"
	"log"
)

// Create two interfaces that have exactly the same signature
// Why? So every struct that implements one automatically implements the other

type interface1 interface {
	Foo() int
}

type interface2 interface {
	Foo() int
}

// Create an impl for the interfaces defined above
type impl int

func (i impl) Foo() int { return int(i) }

// Note that the Fruit is of type "interface2"
// Despite that, it will always go to "interface1" case in the type switch
// It is because anything that implements interface2
// also implements interface1
type Basket struct {
	Fruit interface2
}

func getFruites(basket Basket) {
	switch t := basket.Fruit.(type) {
	case interface1:
		ans, ok := t.(impl)
		if !ok {
			log.Panic("Rotten ans!")
		}
		fmt.Println("Interface1:", ans)
	case interface2:
		ans, ok := t.(impl)
		if !ok {
			log.Panic("Rotten ans!")
		}
		fmt.Println("Interface2:", ans)
	}
}

func main() {
	appleBasket := Basket{
		Fruit: impl(10),
	}

	getFruites(appleBasket)

	orangeBasket := Basket{
		Fruit: impl(10),
	}

	getFruites(orangeBasket)
}

huangapple
  • 本文由 发表于 2017年5月6日 04:50:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/43813553.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定