接口类型返回另一个接口类型的结构体的功能

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

Functions of interface type returning struct of (another) interface type

问题

我正在尝试在两个存储之间创建一个中间层,该层从存储A中获取数据,将其转换为相应类型的存储B,并将其存储起来。由于我需要转换大约50-100种类型,我希望使用map[string]func,根据storageA.Type确定需要调用哪个转换函数。

每个转换函数将返回不同的结构体,这些结构体反映了存储B中的不同类型。这些存储B结构体都实现了一个公共接口,因此可以调用其中的函数。

简而言之,我的问题是,即使StorageBType1实现了接口StorageBType,我也无法将func(StorageAType) StorageBType1强制转换为func(StorageAType) StorageBType

我创建了这个相当冗长的playground,因为我意识到用文字描述问题很棘手。将第38-41行和60-63行注释掉可以使其运行,但我想使用这些行。对于它的大小我很抱歉,但我找不到一个更简洁但又清晰的例子。

请注意,我不得不重新创建我的stackoverflow帐户,所以我认为我没有足够的声望来评论答案。

*编辑:

非常典型。在提问后我意识到如何解决它。在转换函数中返回接口类型而不是具体类型,在这个playground中进行了更改。

英文:

I'm trying to make a middle layer between two storages which fetches from storage A, converts it to a corresponding type of storage B and then stores it. As there are about 50-100 types I need to convert was I hoping to use a map[string]func and based on the storageA.Type determines which converting function I need to call.

Each of these converting functions will return different structs, all reflecting different types in storage B. Each of these storage B structs implements a common interface so they are possible to call functions with.

Boiled down my problem is that I can't cast func(StorageAType) StorageBType1 to func(StorageAType) StorageBType even though StorageBtype1 implements the interface StorageBType.

I've created this rather long playground as I realize describing the problem in words is tricky. Commenting out line 38-41 and 60-63 will make it run but it's those lines I want to use. Sorry for the size of it but I couldn't figure out a less verbose yet clear example.

Please note that I had to recreate my stackoverflow account so I don't think I have the rep to comment on answers.

*edit:

Very typical. Just after asking I realized how to solve it. By returning the interface type in the converter functions instead of the exact type made the change in this playground.

答案1

得分: 2

不同结果类型的函数类型是不同的类型,即使其中一个结果类型实现了另一个类型也不例外。规范:函数类型:

函数类型表示具有相同参数和结果类型的所有函数的集合。

StorageBtype1StorageBType是不同的类型,因此以它们作为结果类型的函数类型也是不同的,而这些函数类型的值不能用作另一个函数类型的值。

只需将所有转换函数的结果类型更改为StorageBType

func TypeA3ToTypeB1(i StorageAType) StorageBType {
    return StorageBType1{i.Type}
}

func TypeA5ToTypeB2(i StorageAType) StorageBType {
    return StorageBType2{i.Type, i.Name}
}

由于所有返回值都实现了StorageBType,这是一个有效的更改,并且不需要更改转换函数的实现。

现在,当然调用这些函数将返回类型为StorageBType的值。如果这对你来说足够了,你就不需要做其他事情。

如果你碰巧需要将返回值作为存储在接口值中的具体类型,你可以使用type assertion(类型断言)

例如:

a := StorageAType{Type:3}
b := TypeA3ToTypeB1(a) // b 的类型是 StorageBType
if b1, ok := b.(StorageBType1); ok {
    // b1 的类型是 StorageBType1,你可以这样使用它:
    fmt.Println("b1.Type:", b1.Type)
} else {
    // b 不是 StorageBType1 类型,或者它是 nil
}

输出:

b1.Type: 3

如果你想测试多个具体类型,可以使用type switch(类型开关)

switch i := b.(type) {
    case nil:
        fmt.Println("nil")
    case StorageBType1:
        // 这里 i 的类型是 StorageBType1,你可以引用它的字段:
        fmt.Println("StorageBType1", i.Type)
    case StorageBType2:
        // 这里 i 的类型是 StorageBType2,你可以引用它的字段:
        fmt.Println("StorageBType2", i.Type, i.Name)
    default:
        fmt.Println("Unhandled type!")
}
英文:

Function types with different result types are different types, it doesn't matter if one of the result type implements the other. Spec: Function types:

> A function type denotes the set of all functions with the same parameter and result types.

StorageBtype1 and StorageBType are different types, so the function types having them as their result types are also different, and a value of one of these function types cannot be used as the value for the other.

Simply change the result types of all converter functions to StorageBType:

func TypeA3ToTypeB1(i StorageAType) StorageBType {
	return StorageBType1{i.Type}
}

func TypeA5ToTypeB2(i StorageAType) StorageBType {
	return StorageBType2{i.Type, i.Name}
}

Since all the return values implement StorageBType, this is a valid change and requires no changes in the implementation of the converter functions.

Now of course calling these functions will return a value of type StorageBType. If this is enough / sufficient for you, you have nothing further to do.

If by any chance you would need the return value as the concrete type that is stored in the interface value, you may use type assertion.

For example:

a := StorageAType{Type:3}
b := TypeA3ToTypeB1(a) // b is of type StorageBType
if b1, ok := b.(StorageBType1); ok {
    // b1 is of type StorageBType1, you may use it like so:
    fmt.Println("b1.Type:", b1.Type)
} else {
    // b is not of type StorageBType1, or it is nil
}

Output:

b1.Type: 3

If you want to test for many concrete types, you may use a type switch:

switch i := b.(type) {
    case nil:
        fmt.Println("nil")
    case StorageBType1:
        // Here i is of type StorageBType1, you may refer to its fields:
        fmt.Println("StorageBType1", i.Type)
    case StorageBType2:
        // Here i is of type StorageBType2, you may refer to its fields:
        fmt.Println("StorageBType2", i.Type, i.Name)
    default:
        fmt.Println("Unhandled type!")
}

huangapple
  • 本文由 发表于 2016年4月22日 16:13:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/36788465.html
匿名

发表评论

匿名网友

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

确定