很遗憾,您的标题问题的答案是否定的:无法将 CollectionType 作为可用作变量或返回类型的独立类型进行陪审。
像 SequenceType 和 CollectionType 这样的协议要求实现它们的类提供类型别名来填充实现细节,例如元素类型,正如您在上面所做的那样。一旦协议添加了这些要求,它就再也不能用作独立类型。您只能根据符合它的特定类来声明接口。 (如果您尝试过解决此问题,您可能还记得看到关于“关联类型要求”的编译器错误不是很有帮助。)
这就是你不会写作的根本原因
func countItems(collection: CollectionType) -> Int { ... }
但必须改写
func countItems<T: CollectionType>(collection: T) -> Int { ... }
后一种形式确保编译器可以访问实现 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)
(这里,UCT = "基础集合类型"。)
ShieldedCollection 仍然具有所有常见的类型别名作为 CollectionType,但由于可以从上下文中推断出这些类型别名,因此您不必显式指定它们。
这种通用方法的缺陷(不幸的是它是一个相当大的缺陷)是底层类型仍然泄漏到 API 中。上例中shieldedFoo的类型是
ShieldedCollection<Array<Int>>
由于您的基础集合是一个自定义对象,即使客户端无法直接访问该类本身,它的名称仍可能会在 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)
在这里,您通过放弃完全的通用性来使返回类型整洁——此版本的 ShieldedCollection 仅适用于作为数组的底层 CollectionType。 (当然,您只需在 UCT 的 typealias 中替换您自己的自定义集合类型。)