Generics 从函数和方法返回受约束的泛型

Generics 从函数和方法返回受约束的泛型,generics,swift,protocols,type-alias,Generics,Swift,Protocols,Type Alias,我想创建一个函数,返回符合协议的对象,但协议使用typealias。给出以下玩具示例: protocol HasAwesomeness { typealias ReturnType func hasAwesomeness() -> ReturnType } extension String: HasAwesomeness { func hasAwesomeness() -> String { return "Sure Does!" }

我想创建一个函数,返回符合协议的对象,但协议使用
typealias
。给出以下玩具示例:

protocol HasAwesomeness {
    typealias ReturnType
    func hasAwesomeness() -> ReturnType
}

extension String: HasAwesomeness {
    func hasAwesomeness() -> String {
        return "Sure Does!"
    }
}

extension Int: HasAwesomeness {
    func hasAwesomeness() -> Bool {
        return false
    }
}
String
Int
已被扩展以符合
hasaweomeness
,并且每个实现了
hasaweomeness()
方法以返回不同的类型

现在我想创建一个类,它返回一个符合
hasaweomeness
协议的对象。我不在乎类是什么,只是我可以发送消息
hasAwesomenss()
。当我尝试以下操作时,会生成一个编译错误:

class AmazingClass: NSObject {
    func returnsSomethingWithAwesomeness(key: String) -> HasAwesomeness {
        ...
    }
}
错误:协议“hasaweomeness”只能用作一般约束,因为它具有自身或关联的类型要求

可以想象,
returnssomethingwithawomeness
的目的是基于
参数返回一个
字符串
Int
。编译器抛出的错误在某种程度上解释了为什么不允许它,但它确实提供了修复语法的见解

func returnsSomethingWithAwesomeness<T: HasAwesomeness>(key: String) -> T
{
    ...
}
错误:类型“T”不符合协议“StringLiteralConverable”

错误:类型“T”不符合协议“IntegerLiteralConverable”


好吧,现在我被卡住了。有人能帮我填补一下我对类型和泛型的理解上的空白吗?可能会让我找到有用的资源吗?

我认为理解这里发生的事情的关键是区分在运行时动态确定的事情和在编译时静态确定的事情。在大多数语言(如Java)中,协议(或接口)都是关于在运行时获取多态行为的,而在Swift中,具有关联类型的协议也用于在编译时获取多态行为,这一点没有帮助

每当您看到一个通用占位符时,比如示例中的
T
,这个
T
的类型将在编译时确定。因此,在您的示例中:

func returnCollectionContainingOne<C: ExtensibleCollectionType where C.Generator.Element == Int>() -> C {

    // this is allowed because the ExtensibleCollectionType procol 
    // requires the type implement an init() that takes no parameters
    var result = C()

    // and it also defines an `append` function that allows you to do this:
    result.append(1)

    // note, the reason it was possible to give a "1" as the argument to
    // append was because of the "where C.Generator.Element == Int" part
    // of the generic placeholder constraint 

    return result
}

// now you can use returnCollectionContainingOne with arrays:
let a: [Int] = returnCollectionContainingOne()

// or with ContiguousArrays:
let b: ContiguousArray = returnCollectionContainingOne()
func返回具有某种意义的方法(键:字符串)->T

是这样说的:
returnsMethingWisaweomeness
是一个可以对任何类型的
T
进行操作的函数,只要
T
符合
Hasaweomeness

但是
T
的填写内容是在
returns点确定的。调用具有某种意义的mething
时–Swift将查看呼叫站点上的所有信息,确定
T
是什么类型,并用该类型替换所有
T
占位符*

因此,假设在调用站点,选择
T
是一个
String
,您可以将
returnsMethingWithsomeness
视为所有出现的占位符
T
都被替换为
String

// giving the type of s here fixes T as a String
let s: String = returnsSomethingWithAwesomeness("bar")

func returnsSomethingWithAwesomeness(key: String) -> String {
    if key == "foo" {
        return "Amazing Foo"
    }
    else {
        return 42
    }
}
注意,
T
替换为
String
,而不是
hasaweomeness
类型
hasaweomeness
仅用作约束–即限制可能的类型
T

当你这样看它时,你会发现
else
中的
return42
毫无意义——你怎么能从一个返回字符串的函数中返回42呢

为了确保
returnsMethingWithsomeness
可以处理
T
最终出现的任何功能,Swift限制您仅使用那些保证在给定约束下可用的功能。在这种情况下,我们所知道的关于
T
的一切就是它符合
hasaweomeness
。这意味着您可以在任何
T
上调用
returnsMethingWithawesomeness
方法,或者将它与另一个函数一起使用,该函数将一个类型约束为
hasaweomeness
,或者将一个
T
类型的变量分配给另一个变量(所有类型都支持赋值),也就是它

您无法将其与其他Ts进行比较(不能保证它支持
==
)。您不能构造新的方法(谁知道
t
是否有合适的初始化方法?)。您不能从字符串或整数文本创建它(这样做需要
t
符合
stringliteralcoverable
integerliteralcoverable
,这并不一定——因此,当您尝试使用这些类型的文本之一创建类型时,会出现这两个错误)

可以编写泛型函数,返回所有符合协议的泛型类型。但是返回的是特定类型,而不是协议,因此不会动态确定哪种类型。例如:

func returnCollectionContainingOne<C: ExtensibleCollectionType where C.Generator.Element == Int>() -> C {

    // this is allowed because the ExtensibleCollectionType procol 
    // requires the type implement an init() that takes no parameters
    var result = C()

    // and it also defines an `append` function that allows you to do this:
    result.append(1)

    // note, the reason it was possible to give a "1" as the argument to
    // append was because of the "where C.Generator.Element == Int" part
    // of the generic placeholder constraint 

    return result
}

// now you can use returnCollectionContainingOne with arrays:
let a: [Int] = returnCollectionContainingOne()

// or with ContiguousArrays:
let b: ContiguousArray = returnCollectionContainingOne()
func returnCollectionContainingOne()->C{
//这是允许的,因为ExtensionType procol
//要求类型实现不带参数的init()
var result=C()
//它还定义了一个“append”函数,允许您执行以下操作:
结果。追加(1)
//请注意,可以将“1”作为参数的原因
//追加是因为“where C.Generator.Element==Int”部分
//通用占位符约束的定义
返回结果
}
//现在,您可以将returnCollectionContainingOne与数组一起使用:
设a:[Int]=returnCollectionContainingOne()
//或使用连续阵列:
设b:ContiguousArray=returnCollectionContainingOne()
将此代码中的
returnCollectionContainingOne
视为两个函数,一个用于
ContiguousArray
,另一个用于
Array
,由编译器在调用它们时自动编写(因此可以将
C
固定为特定类型)。不是一个返回协议的函数,而是两个返回两种不同类型的函数。因此,以同样的方式
returnsMething