加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

Swift类型 – 从函数和方法返回约束泛型

发布时间:2020-12-14 05:58:06 所属栏目:百科 来源:网络整理
导读:我想创建一个返回符合协议的对象的函数,但协议使用一个typealias。给出以下玩具示例: protocol HasAwesomeness { typealias ReturnType func hasAwesomeness() - ReturnType}extension String: HasAwesomeness { func hasAwesomeness() - String { return
我想创建一个返回符合协议的对象的函数,但协议使用一个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已经被扩展为符合HasAwesomeness,并且每个都实现hasAwesomeness()方法返回一个不同的类型。

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

class AmazingClass: NSObject {
    func returnsSomethingWithAwesomeness(key: String) -> HasAwesomeness {
        ...
    }
}

ERROR: Protocol ‘HasAwesomeness’ can only be used as a generic constraint because it has Self or associated type requirements

你可以想象,返回的意图是一个返回String或Int的关键参数。编译器抛出kinda-sorta的错误是有道理的,因为它不允许,但它确实提供了一个洞察修复语法。

func returnsSomethingWithAwesomeness<T: HasAwesomeness>(key: String) -> T
{
    ...
}

好的,我的阅读是方法returnsSomethingWithAwesomeness是一个通用方法,返回具有子类型HasAwesomne??ss的任何类型T。但是,以下实现会引发更多的编译时类型错误:

func returnsSomethingWithAwesomeness<T: HasAwesomeness>(key: String) -> T
{
    if key == "foo" {
        return "Amazing Foo"
    }
    else {
        return 42
    }
}

ERROR: Type ‘T’ does not conform to protocol ‘StringLiteralConvertible’

ERROR: Type ‘T’ does not conform to protocol ‘IntegerLiteralConvertible’

好的,所以现在我被卡住了。有人请帮助填补我对类型和泛型的理解的差距,可能指出我有用的资源?

我认为了解这里发生了什么的关键是区分在运行时动态确定的事情,以及在编译时静态确定的事情。在大多数语言(如Java)中,协议(或接口)都是关于在运行时获取多态性的行为,而在Swift中,相关类型的协议也用于在编译时获取多态性。

每当你看到一个通用的占位符,就像你的例子中的T一样,这个T的填充类型是在编译时确定的。所以,在你的例子中:

func returnsSomethingWithAwesomeness< T:HasAwesomeness>(key:String) – > T

说:returnSomethingWithAwesomeness是一个可以在任何类型T上运行的功能,只要T符合HasAwesomeness。

但是,填写T的内容是在所返回的点上确定的。Swift将会在调用站点查看所有信息,并确定T是什么类型,并替换所有具有该类型的T占位符。

所以假设在调用站点的选择是T是一个String,你可以想到returnSomethingWithAwesomeness被重写,所有出现的占位符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而不是HasAwesomeness的类型。 HasAwesomeness只被用作一个约束 – 也就是限制可能的类型T可以。

当你这样看的时候,你可以看到else中的return 42没有任何意义 – 你如何从返回一个字符串的函数返回42?

为了确保ret??urnSomethingWithAwesomeness可以与任何T一起工作,Swift限制您只使用从给定约束保证可用的那些功能。在这种情况下,我们所有关于T的都是符合HasAwesomeness的。这意味着您可以在任何T上调用returnsSomethingWithAwesomeness方法,或者将另一个函数用于将类型限制为HasAwesomeness,或者将一个类型T的变量分配给另一个(所有类型支持分配),也就是它。

你不能和其他的Ts比较(不保证它支持==)。你不能构造新的(谁知道T是否有适当的初始化方法?)。并且您不能从字符串或整数字面值创建它(这样做将需要T符合StringLiteralConvertible或IntegerLiteralConvertible,这不一定是 – 因此当您尝试使用这些类型之一创建类型时这两个错误的文字)。

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

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()

想想这个代码中的returnCollectionContainingOne真的是两个函数,一个是为ContiguousArray实现的,一个用于Array,由编译器在你调用它们的位置自动编写(因此它可以将C修改为特定类型)。不是一个返回协议的函数,而是返回两种不同类型的两个函数。所以以相同的方式返回SomethingWithAwesomeness不能在运行时基于一些动态参数返回一个String或一个Int,你不能写一个返回数组或连续数组的returnCollectionContainingOne的版本。所有它可以返回的是一个T,在编译时,任何T实际上都可以由编译器填写。

*这是对编译器实际上做的一些过于简单的过滤,但是这对于这个解释来说是有用的。

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读