【问题标题】:Compilation error with generics and closure to initialize a class instance使用泛型和闭包初始化类实例的编译错误
【发布时间】:2014-07-14 22:41:01
【问题描述】:

我要做的是将闭包注入到类初始化器中,以便我可以从外部控制它的初始化

我已经定义了一个协议,它必须由我要初始化的类来实现:

protocol EntityInitializable {
    typealias T : EntityInitializable
    init(initializer: (T) -> ())
}

和一个试图注入闭包的函数

class EntityMapper<T : EntityInitializable> {
    func f() -> T {
        var cb = {(entity: T) -> () in
        }

        return T(cb)
    }
}

这会在编译过程中产生以下错误:

错误:无法将表达式的类型“T.T”转换为类型“T” 返回 T(cb)

如果我修改上面的代码去掉泛型部分,比如:

class MyClass {
    init(initializer: (MyClass) -> ()) {
    }
}

class EntityMapper {
    func f() -> MyClass {
        var cb = {(entity: MyClass) -> () in
        }

        return MyClass(cb)
    }
}

它有效。

有什么想法吗?可能是因为我试图通过创建泛型实例并传递带有相同泛型类型参数的闭包来递归使用泛型?

【问题讨论】:

  • 那些 T 真的很混乱,你应该给你的类型参数起有意义的名字。虽然在泛型中为了简洁起见,但如果不鼓励使用 T 是可以接受的,但在定义类型别名时,最好给它们一个名称,以明确类型代表什么(想象一下,如果其他人必须实现该协议,她将如何能够理解使用什么类型?)
  • 这是正确的,从使用的角度来看我同意 - 但通常在开发过程中,当我看到 T 或 V 时,我会立即知道它是通用名称,而使用更有意义的名称并不那么明显......

标签: swift


【解决方案1】:

您的代码不起作用的原因是没有任何内容表明 T 在 EntityInitializable(为了清楚起见,我将其称为EntityType)必须等于实现协议的类型。下面是一个可以说明问题的示例:

class A: EntityInitializable {
    typealias EntityType = B
    init(initializer: (EntityType) -> ()) {

    }
}

class B: EntityInitializable {
    // [...]
}

根据您编写的代码,这完全没问题,这就是编译器不接受 cb 闭包的原因。其entity 参数的类型为T,而EntityInitializable 期望其类型为T.EntityType

编写此代码的正确方法是使用Self,它表示实现协议的类:

protocol EntityInitializable {
    init(initializer: (Self) -> ())
}

【讨论】:

  • 有趣...我在文档中没有发现任何提及Self,你有任何链接吗?
  • 不,但目前 Swift 的文档充其量是不完整的,尤其是关于元编程。不过,Self 用于该语言的所有内置协议。例如,尝试在 Xcode 窗口中编写 Equatable 并 Cmd 单击它。
  • 谢谢 - 经过测试,它可以工作。现在需要深入研究,我想了解更多
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-03-04
  • 2020-12-03
  • 1970-01-01
  • 2012-11-20
  • 1970-01-01
相关资源
最近更新 更多