Swift: use a wildcard as a type parameter

I would like to store class instances derived from a generic type in a dictionary; that is, the dictionary must store an instance of any class derived from this common one.

Something like that:

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") } } 

In Java, this is pretty simple:

 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); } } } 

Is something similar possible in Swift? The only way I got this is to simply create a dictionary with "Any" as the value type. But then I lose some type security, so I would like to avoid this solution if possible.

+6
source share
2 answers

I don’t think you can imitate the Java pattern, but you might not need it. You can simply use ChildClass wherever the code expects a ParentClass . So using your example:

 class TestGenerics { var dict: Dictionary<String, GenericClass<ParentClass>> = [:] func test() { var derivedClassInstance = GenericClass<ParentClass>() dict.updateValue(derivedClassInstance, forKey: "name") } } 

Now just use ChildClass to populate foo

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

This code compiles without errors. However, Swift does not support covariance in custom generic classes, so you cannot change the dictionary using the covariant type, so the following does not compile:

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

This is not very intuitive, since covariance is really supported on the native array and dictionaries, so this is allowed:

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

But not this:

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

Basically, Swift considers Array<ChildClass> be a subtype of Array<ParentClass> , but at the moment it is not possible to tell the compiler that GenericClass<ChildClass> is (or should be) a subtype of GenericClass<ParentClass> . Hopefully as the language develops, a way will be added to declare custom classes as covariant.

0
source

You need to parameterize a type containing any link that will be made universal, and use a type constraint there. In addition, typealias refers to the restrictions of a parameterized type, so it is included in this type.

 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] 
-1
source

Source: https://habr.com/ru/post/973723/


All Articles