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?
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 .
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.
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.
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With