Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot assign value of generic type

I have generic class - Class1.

class Class1<T> {
}

And I have Class2 with variable object1 of type Class1 and generic type UIView

class Class2 {
    var object1: Class1<UIView>?
}

When I create instance of Class2 and try to assign to object1 instance of type Class1 and generic type UITableView I got an error: "Cannot assign value of type Class1<UITableView> to type Class1<UIView>"

var c = Class2()
c.object1 = Class1<UITableView>()

However the same logic works for Array. Why?

enter image description here

like image 470
Evgeny Mikhaylov Avatar asked Dec 26 '16 08:12

Evgeny Mikhaylov


People also ask

What can not be a generic type?

Almost all reference types can be generic. This includes classes, interfaces, nested (static) classes, nested interfaces, inner (non-static) classes, and local classes. The following types cannot be generic: Anonymous inner classes .

What are generic methods *?

Generic methods are methods that introduce their own type parameters. This is similar to declaring a generic type, but the type parameter's scope is limited to the method where it is declared. Static and non-static generic methods are allowed, as well as generic class constructors.

Why is parameterized type important?

Types defined using parameters are called parameterized types. Parameterized types perform an important role in Haskell, as they allow you to define generic data structures that work with a wide range of existing data.


1 Answers

Let me elaborate on Anton’s comment. The question is when you can use a B for an A. Usually you can do that when B is a subtype of A. For instance, you can assign a UITableView to a variable of type UIView.

So when is something a subtype of something else?

For classes, this is straightforward: if you subclass B from A, B is a subtype of A.

For function types, you need to consider the argument types and the return type. A function type F2 is a subtype of function type F1 if F2’s parameter types are supertypes of F1’s parameter and F2’s return type is a subtype of F1’s return type. You could say that a function must not require more (i.e. it must not require subtypes as parameters but can require supertypes) and must not provide less (i.e. it must not return a supertype but can return a subtype) for its type to be a subtype. The terminology is that the parameter types must be contravariant and the return type must be covariant.

Example:

var f1: UIControl -> UIControl = ...

let f2: UIView -> UIControl = ...
let f3: UIControl -> UIButton = ...
let f4: UIView -> UIButton = ...
f1 = f2  // Fine, f2 takes UIView so it also takes UIControl
f1 = f3  // Fine, f3 returns UIButton which is a UIControl
f1 = f4  // Fine, both of the above

let f5: UIButton -> UIControl
let f6: UIControl -> UIView
let f7: UIButton -> UIView
f1 = f5  // Error, couldn’t call with a UIControl because f5 demands at least a UIButton
f1 = f6  // Error, call would return only a UIView
f1 = f7  // Error, both of the above

So the types of f2, f3, and f4 are subtypes of f1’s type and the types of f5, f6, and f7 are not.

Now what about generic types? In Swift, custom types with type parameters are all invariant. That is, in your example no Class1<T2> object can be used as a Class1<T1> object, no matter the relation between T1 and T2 (except when T1 and T2 are the same type).

However, Swift has indeed some built-in variance rules that makes your example with arrays work: [UITableView] (Array<UITableView>) is a subtype of [UIView] (Array<UIView>). Note that the same is true for optionals, i.e. UITableView? (Optional<UITableView>) is a subtype of UIView? (Optional<UIView>). So both arrays and optionals are covariant with their type parameter.

Further reading:

  • Liskov Substitution Principle on Wikipedia
  • Mike Ash on Covariance and Contravariance
like image 141
thm Avatar answered Oct 03 '22 23:10

thm