Generics in Kotlin

Tram Ho

Generics means that we use a class or an implementation in a very general way. For example, the List interface allows us to reuse code. We can create a String list, an integer list, and we will have the same operation even when the types are different. So the list is like a common function for every implementation.

Kotlin allows you to use parameters for methods and attributes, creating parameterized classes.

1. Type vs Class vs Subtype

  • Type describes the properties that a collection of objects can share.
  • Class is just an implementation of that Type.
  • A subtype must accept at least the same types of types as its supertype declaration.
  • A subtype must return at most the same types of types as its supertype declaration.

2. Variance

Variance says how subtyping between types is more complicated with regards to subtyping between their components.

The convention: E = element | T = type | K = key | V = value

Code Sample:

3. Covariance

  • If C <T> is a generic type whose parameter T and U are subtypes of T, then C <U> is a subtype of C <T>
  • For example, List <Int> is a subtype of List <Number> because Int is a subtype of Number.
  • Applies to types that are “producers”, or “sources” of T
  • T only appears in the “out” position, that is, the return type of the function

Code Sample:

4. Contravariance

  • If C <T> is a generic type whose parameter T and U are subtypes of T, then C <T> is a subtype of C <U>
  • U is the subtype of T ⇒ C <T> is the subtype of C <U>
  • For example: Function1 <Number, Int> is a subtype of Function1 <Int, Int> because Int is a subtype of Number.
  • Applies to styles that are “consumers” of T.
  • T only appears in the “in” position, that is, the type of the function argument

Code Sample:

5. Invariance

  • If C <T> is a subtype of C <U>, then T = U
  • For example: Array <T> is invariant in T
  • T appears in both “in” and “out” positions.
  • Type is both a producer and a consumer of T
  • Remember: Lambdas are contra-variant in argument types and covariant in their return types.

6. Type projections

  • The projection type is a type that has been limited in certain ways to obtain variance characteristics by using the use-site variance.

7. Star projection

  • They are useful when we know nothing about the type of arguments, but need to use them safely.
  • The safe way here is to define a generic type projection, that every specific initialization of that generic type will be a subtype type of that projection.
  • Instead of using C <in Nothing> or C <out Any?>, You can use C <*>. It creates an effective interface similar to the other two approaches:
  • So we have three possible ways of accepting any kind of generic:
    • in-projection – <in Nothing>
    • out-projection – <out Any?>
    • star-projection – <*>

Code Sample:

8. Type erasure and reified type parameters

  • Java has restrictions on types that are considered to be re-determinable – meaning they are completely available at run time (see Java SE for reifiable types).
  • The type safety tests that Kotlin performs for the use of generic declarations are only performed at compile time. At runtime, generic type instances do not contain any information about their actual type arguments.
  • Execute type constraints only at compile time and remove element type information at runtime.
  • The type information is assumed to be deleted.
  • In the case of type parameters in Kotlin, we can compare types and get Class objects.
  • Reified types parameters:
    • Only works with functions (or extension properties with function get ())
    • Working with functions is declared as inline
  • Advantages of using Reified type parameters:
    • Type checking with is
    • casts without cast unchecked warning
    • assign class objects by appending :: class.java to the parameter name. For example: val a = T :: class.java

Code Sample:

Ref: https://magdamiu.com/2020/06/21/generics-in-kotlin/

Share the news now

Source : Viblo