在Swift中约束一个泛型和另一个泛型

时间:2015-12-27 19:09:08

标签: swift generics protocols generic-constraints

我遇到了一个问题,我有一些协议:

protocol Baz {
    func bar<T>(input:T)
}

函数bar是通用的,因为我不希望协议本身具有Self(它需要在集合中可用)。我有一个定义为:

的协议的实现
class Foo<S>: Baz {
    var value:S

    init(value:S) {
        self.value = value
    }

    func bar<T>(input:T) {
        value = input
    }
}

这会产生错误,因为编译器不知道ST是同一类型。理想情况下,我应该能够写出类似的内容:

    func bar<T where T==S>(input:T) {
        value = input
    }

    func bar<T:S>(input:T) {
        value = input
    }

第一种形式给出了#34;相同类型的要求使得通用参数&#39; S&#39;和&#39; T&#39;等效&#34;错误(这正是我试图做的,所以不确定为什么它是一个错误)。第二种形式给了我一个&#34;继承自非协议,非类型&#39;&#39;&#34;&#34;

关于如何使其工作的任何想法,或Swift中更好的设计模式?

更新:正如@ luk2302指出的那样,我忘了让Foo遵守Baz协议

2 个答案:

答案 0 :(得分:2)

@ luk2302在评论中暗示了很多内容,但只是为了让未来的搜索者明白。

protocol Baz {
    func bar<T>(input:T)
}

这个协议几乎肯定没用。它实际上与以下协议相同(也几乎完全没用):

protocol Baz {
    func bar(input:Any)
}

你很可能意味着(并暗示你的意思):

protocol Baz {
    typealias T
    func bar(input: T)
}

如您所知,这使协议成为PAT(具有关联类型的协议),这意味着您无法将直接放入集合中。正如您所注意到的,如果您确实需要它们的集合,通常的解决方案是type eraser。如果Swift会为你自动编写橡皮擦,这将很有可能在将来做,并且可以消除这个问题。虽然说虽然有点单调乏味,但写入类型的橡皮擦非常简单。

现在,虽然您无法将PAT直接放入集合中,但您可以将通用约束的PAT放入集合中。因此,只要将集合包装为约束T的类型,它仍然没有问题。

如果这些变得复杂,约束代码可能变得乏味且非常重复。然而,这可以通过许多技术来改进。

使用静态方法的通用结构可以避免重复提供对自由函数的约束。

协议可以转换为通用结构(这将类型橡皮擦形式化为主要类型,而不是&#34;根据需要&#34;)。

在许多情况下,协议可以用函数替换。例如,鉴于此:

protocol Bar {
    typealias T
    func bar(input: T)
}

struct Foo : Bar {
    func bar(input: Int) {}
}

你不能这样做:

 let bars: [Bar] = [Foo()] // error: protocol 'Bar' can only be used as a generic constraint because it has Self or associated type requirements

但你可以很容易地做到这一点,这也很好:

let bars = [(Int) -> Void] = [Foo().bar]

这对单方法协议来说特别有用。

协议,泛型和函数的混合比尝试将所有内容强制进入协议框要强大得多,至少在协议添加一些缺少的功能以实现其承诺之前。

(对特定问题提出具体建议会更容易。没有一个答案可以解决所有问题。)

答案 1 :(得分:0)

已编辑的解决方法“......错误,因为编译器不知道S和T是同一类型。”

首先:这只是一个单独的注释(也许是为了兑换我以前的答案,最终是我自己追逐自己的尾巴来计算大量的冗余代码)以及Robs的优秀答案。 / p>

以下解决方法可能会让您的实施protocol Foo ... / class Bas : Foo ...模仿您最初要求的行为,因为类方法bar(...)会知道泛型S并且T实际上是相同的类型,而在S T相同类型的情况下,Foo仍然符合协议。< / p>

protocol Baz {
    func bar<T>(input:T)
}

class Foo<S>: Baz {
    var value:S

    init(value:S) {
        self.value = value
    }

    func bar<T>(input:T) {
        if input is S {
            value = input as! S
        }
        else {
            print("Incompatible types. Throw...")
        }
    }
}

// ok
var a = Foo(value: 1.0) // Foo<Double>
print(a.value) // 1.0
a.bar(2.0)
print(a.value) // 2.0
let myInt = 1
a.bar(myInt) // Incompatible types. Throw...
print(a.value) // 2.0

// perhaps not a loophole we indended
let myAny : Any = 3.0
a.bar(myAny)
print(a.value) // 3.0

此处的AnyAnyObject漏洞可以通过创建一个虚拟类型约束来兑换,您可以扩展所有类型(您希望使用泛型),但不能扩展{{1} }和Any

AnyObject

然而,这完全排除了通用中的protocol NotAnyType {} extension Int : NotAnyType {} extension Double : NotAnyType {} extension Optional : NotAnyType {} // ... protocol Baz { func bar<T: NotAnyType>(input:T) } class Foo<S: NotAnyType>: Baz { var value:S init(value:S) { self.value = value } func bar<T: NotAnyType>(input:T) { if input is S { value = input as! S } else { print("Incompatible types. Throw...") } } } // ok var a = Foo(value: 1.0) // Foo<Double> // ... // no longer a loophole let myAny : Any = 3.0 a.bar(myAny) // compile time error let myAnyObject : AnyObject = 3.0 a.bar(myAnyObject) // compile time error Any(不仅仅是“漏洞投射”),这可能不是一个受追捧的行为。