调用 Swift 协议初始化器
Invoking Swift Protocol Initializers
Swift 5,在 XCode 11.2.1。我一直在 Java 中编程,目前在 Swift 方面只是中等水平,所以我的一些理解被 Java 习语所影响。
假设我们提供一个协议,或者可能是一个伪抽象class,它可以在一个单独的模块中实现。然后程序员将对 class 的引用传递回第一个模块中的代码,该模块实例化 class。现在,在 Java 中这很困难,因为您无法保证 subclass 将定义什么 initializers/static 方法。但是,在 Swift 中,您可以。这很整洁。除了,我还没有找到使用它们的方法。我 期望 的是您可以使用
这样的代码
protocol FooP {
init(s: String)
}
func instantiate(clazz: FooP.Type) -> FooP {
return clazz.init(s: "qwerty")
}
...OOOOORRRR 确实有效,正如我所希望的那样。我想在我的测试中我只是没能找到 CLASS/.Type/.self 的正确组合来提示我成功的可能性。
由于我还没有找到关于这种可能性的任何其他信息,尽管搜索了 30-60 分钟,我还是会回答我自己的问题。
事实证明,您可以使用协议提供的静态方法,即使涉及的 类(不是其实例化)被强制转换为它们的协议。您甚至可以使用泛型使示例 instantiate
方法更方便地具有您提供的类型的 return 类型,尽管这会导致不同的问题,如下所示。这里有一堆代码演示了你能做什么和不能做什么:
public protocol ProtocolTest {
init(s: String)
static func factory(s: String) -> Self
}
public class Foo : ProtocolTest, CustomStringConvertible {
public let s: String
public required init(s: String) {
self.s = s
}
public class func factory(s: String) -> Self {
return Self(s: s)
}
public var description: String { return "{\(Self.self)|s:\"\(s)\"}" }
}
public class Bar : Foo {
}
public func instantiateGeneric<T : ProtocolTest>(clazz: T.Type) -> T {
return clazz.init(s: "qwertyGeneric")
}
public func instantiateNongeneric(clazz: ProtocolTest.Type) -> ProtocolTest {
return clazz.init(s: "qwertyNongeneric")
}
public func run() {
let ptts: [ProtocolTest.Type] = [Foo.self, Bar.self]
// Success
let aInit : Bar = Bar.init(s: "qwertyInit")
// Success
let aGeneric : Bar = instantiateGeneric(clazz: Bar.self)
// Compile error: Cannot convert value of type 'ProtocolTest' to specified type 'Bar'
let aNongeneric : Bar = instantiateNongeneric(clazz: Bar.self)
for ptt in ptts {
// Success
let bInit : ProtocolTest = ptt.init(s: "qwertyInit")
// Compile error: Protocol type 'ProtocolTest' cannot conform to 'ProtocolTest' because only concrete types can conform to protocols
let bGeneric1 : ProtocolTest = instantiateGeneric(clazz: ptt)
// Compile error: Cannot invoke 'instantiateGeneric' with an argument list of type '(clazz: ProtocolTest.Type)'
let bGeneric2 = instantiateGeneric(clazz: ptt)
// Success
let bNongeneric : ProtocolTest = instantiateNongeneric(clazz: ptt)
// This works too, btw:
let bFactory : ProtocolTest = ptt.factory(s: "qwertyFactory")
}
}
我不确定循环中的 instantiateGeneric
究竟是怎么回事。这似乎是一行合理的代码。如果您有任何解释,请告诉我。也许关于 <T : ProtocolTest>
,ProtocolTest
在技术上不符合 ProtocolTest
,或者它必须是一个严格的子类?不确定。能够对两种用途使用相同的方法会很好,但我已经对我能做的事情感到非常满意。这个发现可能足以让我真正喜欢Swift,哈哈。
Swift 5,在 XCode 11.2.1。我一直在 Java 中编程,目前在 Swift 方面只是中等水平,所以我的一些理解被 Java 习语所影响。
假设我们提供一个协议,或者可能是一个伪抽象class,它可以在一个单独的模块中实现。然后程序员将对 class 的引用传递回第一个模块中的代码,该模块实例化 class。现在,在 Java 中这很困难,因为您无法保证 subclass 将定义什么 initializers/static 方法。但是,在 Swift 中,您可以。这很整洁。除了,我还没有找到使用它们的方法。我 期望 的是您可以使用
这样的代码protocol FooP {
init(s: String)
}
func instantiate(clazz: FooP.Type) -> FooP {
return clazz.init(s: "qwerty")
}
...OOOOORRRR 确实有效,正如我所希望的那样。我想在我的测试中我只是没能找到 CLASS/.Type/.self 的正确组合来提示我成功的可能性。
由于我还没有找到关于这种可能性的任何其他信息,尽管搜索了 30-60 分钟,我还是会回答我自己的问题。
事实证明,您可以使用协议提供的静态方法,即使涉及的 类(不是其实例化)被强制转换为它们的协议。您甚至可以使用泛型使示例 instantiate
方法更方便地具有您提供的类型的 return 类型,尽管这会导致不同的问题,如下所示。这里有一堆代码演示了你能做什么和不能做什么:
public protocol ProtocolTest {
init(s: String)
static func factory(s: String) -> Self
}
public class Foo : ProtocolTest, CustomStringConvertible {
public let s: String
public required init(s: String) {
self.s = s
}
public class func factory(s: String) -> Self {
return Self(s: s)
}
public var description: String { return "{\(Self.self)|s:\"\(s)\"}" }
}
public class Bar : Foo {
}
public func instantiateGeneric<T : ProtocolTest>(clazz: T.Type) -> T {
return clazz.init(s: "qwertyGeneric")
}
public func instantiateNongeneric(clazz: ProtocolTest.Type) -> ProtocolTest {
return clazz.init(s: "qwertyNongeneric")
}
public func run() {
let ptts: [ProtocolTest.Type] = [Foo.self, Bar.self]
// Success
let aInit : Bar = Bar.init(s: "qwertyInit")
// Success
let aGeneric : Bar = instantiateGeneric(clazz: Bar.self)
// Compile error: Cannot convert value of type 'ProtocolTest' to specified type 'Bar'
let aNongeneric : Bar = instantiateNongeneric(clazz: Bar.self)
for ptt in ptts {
// Success
let bInit : ProtocolTest = ptt.init(s: "qwertyInit")
// Compile error: Protocol type 'ProtocolTest' cannot conform to 'ProtocolTest' because only concrete types can conform to protocols
let bGeneric1 : ProtocolTest = instantiateGeneric(clazz: ptt)
// Compile error: Cannot invoke 'instantiateGeneric' with an argument list of type '(clazz: ProtocolTest.Type)'
let bGeneric2 = instantiateGeneric(clazz: ptt)
// Success
let bNongeneric : ProtocolTest = instantiateNongeneric(clazz: ptt)
// This works too, btw:
let bFactory : ProtocolTest = ptt.factory(s: "qwertyFactory")
}
}
我不确定循环中的 instantiateGeneric
究竟是怎么回事。这似乎是一行合理的代码。如果您有任何解释,请告诉我。也许关于 <T : ProtocolTest>
,ProtocolTest
在技术上不符合 ProtocolTest
,或者它必须是一个严格的子类?不确定。能够对两种用途使用相同的方法会很好,但我已经对我能做的事情感到非常满意。这个发现可能足以让我真正喜欢Swift,哈哈。