gnt*_*skn 6 generics protocols swift
我已经实现了一个自定义队列对象,我想用它来存储控制器中的先前项目.我想在我的控制器中将此类型用作私有变量,并且仅将其作为简单的CollectionType兼容对象公开给外部,以便客户端可以迭代或索引对象,而无需知道任何特定于类的详细信息,例如clear()函数.
Swift协议不能是通用的,所以不幸的是我不能简单地定义一个getter来返回一个CollectionOf<Type>.我已经使用以下抽象基类实现了这种行为,并从中继承了我的集合,但我希望可能有更多的Swift-y和内置方法来实现这一点,希望也不需要子类化:
class AnyCollectionOf<MemberT, IndexT: ForwardIndexType>: CollectionType {
// Sequence Type
func generate() -> GeneratorOf<MemberT> {
fatalError("must override")
}
// Collection Type
typealias Index = IndexT
typealias Element = MemberT
subscript (index: Index) -> Element {
get {
fatalError("must override")
}
}
var startIndex: Index {
get {
fatalError("must override")
}
}
var endIndex: Index {
get {
fatalError("must override")
}
}
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,您的标题问题的答案是否定的:没有办法将 CollectionType 临时设置为可用作变量或返回类型的独立类型。
像 SequenceType 和 CollectionType 这样的协议要求实现它们的类提供类型别名来填充实现细节,例如元素类型,正如您上面所做的那样。一旦协议添加了这些要求,它就再也不能用作独立类型。您只能根据符合接口的特定类来声明接口。(如果您尝试解决此问题,您可能记得看到过有关“关联类型要求”的不太有用的编译器错误。)
这就是你写不出来的根本原因
func countItems(collection: CollectionType) -> Int { ... }
Run Code Online (Sandbox Code Playgroud)
但必须改为写
func countItems<T: CollectionType>(collection: T) -> Int { ... }
Run Code Online (Sandbox Code Playgroud)
后一种形式确保编译器可以访问实现 CollectionType 协议的对象的实际类型 (T)。
然而,如果您从封装而不是继承的角度思考,那么您想要做的事情可能仍然有一个更清晰的实现。您可以使用一个简单的包装器来阻止对除核心 CollectionType 方法之外的所有内容的访问:
struct ShieldedCollection<UCT: CollectionType> : CollectionType
{
private var underlying: UCT
func generate() -> UCT.Generator { return underlying.generate() }
subscript(index: UCT.Index) -> UCT.Generator.Element { return underlying[index] }
var startIndex: UCT.Index { return underlying.startIndex }
var endIndex: UCT.Index { return underlying.endIndex }
}
var foo = [1, 2, 3]
var shieldedFoo = ShieldedCollection(underlying: foo)
Run Code Online (Sandbox Code Playgroud)
(此处,UCT =“底层集合类型”。)
ShieldedCollection 仍然具有作为 CollectionType 的所有常见类型别名,但由于这些别名可以从上下文中推断出来,因此您不必显式指定它们。
这种通用方法的缺陷(不幸的是,这是一个相当大的缺陷)是底层类型仍然泄漏到 API 中。上例中shieldedFoo的类型是
ShieldedCollection<Array<Int>>
Run Code Online (Sandbox Code Playgroud)
由于您的底层集合是自定义对象,因此即使客户端无法直接访问类本身,其名称仍然可能在 API 中泄漏。但请注意,这不是功能问题,因为不可能通过 ShieldedCollection 包装器访问底层对象。此外,消费者永远不必自己编写类型 - 他们只需使用 previousItems() 的结果作为 CollectionType,编译器就会解开所有内容。
如果您确实想隐藏所有对底层集合类型的提及,则可以通过将 UCT 定义移动到 ShieldedCollection 内来编写上面包装器的特定于任务的模拟:
struct ShieldedCollection<T> : CollectionType // Changed!
{
typealias UCT = [T] // Added!
private var underlying: UCT // Everything else identical
func generate() -> UCT.Generator { return underlying.generate() }
subscript(index: UCT.Index) -> UCT.Generator.Element { return underlying[index] }
var startIndex: UCT.Index { return underlying.startIndex }
var endIndex: UCT.Index { return underlying.endIndex }
}
var foo = [1, 2, 3]
var shieldedFoo = ShieldedCollection(underlying: foo)
Run Code Online (Sandbox Code Playgroud)
在这里,您通过放弃完全的通用性来使返回类型整洁——此版本的 ShieldedCollection 仅适用于作为数组的底层 CollectionType。(当然,您只需将类型别名中的 UCT 替换为您自己的自定义集合类型即可。)
| 归档时间: |
|
| 查看次数: |
1212 次 |
| 最近记录: |