Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift: Use wildcard as generic type parameter

I'd like to store instances of a class derived from a generic type in a Dictionary; that is, the Dictionary should store an instance of any class derived from this generic.

Something like this:

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

This is fairly straightforward in 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);

        }
    }

}

Is something like this possible in Swift? The only way I've gotten this to work is to just create a Dictionary with "Any" as the value type. But, then I lose some type safety, so I'd like to avoid this solution if possible.

like image 577
Anna Dickinson Avatar asked Aug 12 '14 20:08

Anna Dickinson


People also ask

What are generic wildcards?

In generic code, the question mark (?), called the wildcard, represents an unknown type. The wildcard can be used in a variety of situations: as the type of a parameter, field, or local variable; sometimes as a return type (though it is better programming practice to be more specific).

Can wildcard types be replaced by any type?

Due to extensive capture conversion, in most places, compiler treats wildcards as if they are type variables. Therefore indeed programmer can replace wildcard with type variables in such places, a sort of manual capture conversion.

What is a wildcard parameterized type?

In the Java programming language, the wildcard ? is a special kind of type argument that controls the type safety of the use of generic (parameterized) types. It can be used in variable declarations and instantiations as well as in method definitions, but not in the definition of a generic type.


1 Answers

I don't think you can mimic Java's wildcard, but you might not need to either. You can just use ChildClass wherever the code is expecting 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 fill in foo

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

That code compiles without errors. However, Swift doesn't support covariance on custom generics classes so you can't change the dictionary with a covariant type, so the following doesn't compile:

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

This is not very intuitive, since covariance is indeed supported on 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 treats Array<ChildClass> as a subtype of Array<ParentClass> but it isn't possible at the moment to tell the compiler that GenericClass<ChildClass> is (or should be) a subtype of GenericClass<ParentClass>. Hopefully, as the language evolves, a way to declare custom classes as covariant will be added.

like image 130
asayagogalvan Avatar answered Oct 16 '22 13:10

asayagogalvan