Swift:使用通配符作为泛型类型参数

时间:2014-08-12 20:10:59

标签: generics swift wildcard

我想在Dictionary中存储从泛型类派生的类的实例;也就是说,Dictionary应该存储从这个泛型派生的任何类的实例。

这样的事情:

class ParentClass {}
class ChildClass: ParentClass {}

class GenericClass<T: ParentClass> {
    var foo:T?
}

typealias DerivedClass = GenericClass<ChildClass>

class TestGenerics {

    var dict: Dictionary<String, GenericClass<**what goes here??**>> = [:]

    func test() {
        var derivedClassInstance = DerivedClass()
        dict.updateValue(derivedClassInstance, forKey: "name")
    }
}

这在Java中非常简单:

public class TestGenericsOuter {

    class ParentClass {}
    class ChildClass extends ParentClass {}

    class GenericClass<T extends ParentClass> {
        T foo;
    }

    class DerivedClass extends GenericClass<ChildClass> {}

    class TestGenerics {

        Dictionary<String, GenericClass<? extends ParentClass>> dict;

        void test() {
            DerivedClass derivedClassInstance = new DerivedClass();
            dict.put("name", derivedClassInstance);

        }
    }

}

在Swift中这样的事情可能吗?我实现这一点的唯一方法是创建一个以“Any”作为值类型的Dictionary。但是,我失去了一些类型的安全性,所以如果可能的话,我想避免这种解决方案。

2 个答案:

答案 0 :(得分:0)

我认为你不能模仿Java的通配符,但你可能也不需要。您可以在代码期望ChildClass的任何地方使用ParentClass。所以使用你的例子:

class TestGenerics {
    var dict: Dictionary<String, GenericClass<ParentClass>> = [:]

    func test() {
        var derivedClassInstance = GenericClass<ParentClass>()
        dict.updateValue(derivedClassInstance, forKey: "name")
    }
}

现在只需使用ChildClass填写foo

let test = TestGenerics()
test.test()
test.dict["name"]?.foo = ChildClass()

该代码编译没有错误。但是,Swift不支持自定义泛型类的协方差,因此您无法使用协变类型更改字典,因此以下内容无法编译:

test.dict = Dictionary<String, GenericClass<ChildClass>>()
//Error: 'ChildClass' is not identical to 'ParentClass'

这不是很直观,因为本机数组和字典确实支持协方差,所以这是允许的:

let array: Array<ParentClass> = Array<ChildClass>()
let dic: Dictionary<String, ParentClass> = Dictionary<String, ChildClass>()

但不是这样:

let generic: GenericClass<ParentClass> = GenericClass<ChildClass>()
//Error: 'ChildClass' is not identical to 'ParentClass'

基本上Swift将Array<ChildClass>视为Array<ParentClass>的子类型,但目前无法告诉编译器GenericClass<ChildClass>是(或应该)GenericClass<ParentClass>的子类型1}}。希望随着语言的发展,将添加一种将自定义类声明为协变的方法。

答案 1 :(得分:-1)

您需要参数化包含任何引用的类型,并在那里使用类型约束。此外,typealias与参数化类型的约束有关,因此它在该类型内部。

class ParentClass {}
class ChildClass: ParentClass {}

class GenericClass<T: ParentClass> {
    var foo:T?
}


class TestGenerics<T: ParentClass> {
    typealias DerivedClass = GenericClass<T>

    var dict: Dictionary<String, GenericClass<T>> = [:]

    func test() {
        var derivedClassInstance = DerivedClass()
        dict.updateValue(derivedClassInstance, forKey: "name")
    }
}

let blah = TestGenerics<ChildClass>()
let baz = GenericClass<ChildClass>()
baz.foo = ChildClass()
blah.dict = ["a":baz]