Kotlin Foundation: generics

Keywords: Mobile Java

Why generics

  • Efficiency, cost
  • Reduce template code writing

Classification of generics

  • Generic class
  • generic method

Keywords of generics

3.1 T

T represents any type, which can be any string, generally: T, U, S, E, K, V.

  • T, U, S: any type
  • E: element type of collection
  • K, V: key and value types of Map

3.2 out

Wildcard, equivalent to Java's "extend"

3.3 in

Wildcard, equivalent to Java's super

Definition of generic classes and methods

4.1 definition of generic classes

class AClass<T> ...
class AClass<out T> ...
class AClass<in T> ...

< T > declared after the class name. In this class, you can

  • Use T as the type declaration of an object

val a: T;

  • Use T as a type parameter for a generic type object

List<T> list;

  • Use T as the declaration before the return type of the method (so the generic method in the generic class can omit the declaration before the return type)

See definition of generic methods

4.2 definition of generic methods

fun <T> ...

< T > declared before method return type. In this method, you can

  • Use T as the return type of the method
fun <T> foo() : T ...
  • Use T as the parameter type of the method
fun <T> foo(t: T) ...
  • Use T as the type parameter of the generic parameter of the method
fun <T> foo(list: List<T>) ...

V. use of generic classes and methods

Use is the instantiation of a generic class.

5.1 generic implementation class

It refers to a single class specified as a type parameter. For example, String corresponds to aclass < String >.

A generic implementation class, the type parameter is String (i.e. aclass < T >, when used, t has been specified as String).

5.2 general configuration

5.2.1 AClass< out BClass >

It represents a batch of generic implementation classes, which are characterized by:

  • Their type parameters can only be subclasses of BClass.
  • They are all subclasses of aclass < out bclass >.

This kind of parent-child relationship between generic implementation class and generic class is the same as that between generic implementation class type parameter and generic class type parameter, which is called covariant. When the implementation class is changed to aclass < out bclass >:

  • BClass can be got, because the common upper bound of type parameters of all implementation classes is BClass.
  • You cannot set any values because you are not sure which BClass subclass the type parameter of the implementation class is.
5.2.2 AClass< in BClass >

It represents a batch of generic implementation classes, which are characterized by:

  • Their type parameters can only be the parent of BClass.
  • They are all subclasses of AClass.

This kind of parent-child relationship between the generic implementation class and the generic implementation class, and the opposite relationship between the type parameters of the generic implementation class and the type parameters of the generic implementation class, is called inversion. When the implementation class is converted to atlas:

  • You can't get BClass, you can only get Object, because the common upper bound of type parameters of all implementation classes is only Object.
  • Bclass and its subclasses can be set because they must cover the type parameters of the real implementation class.

5.2.3 out and in in class definition

For the convenience of development in Kotlin, out and in can be used or defined, such as class aclass < out t >.

The definition of class aclass < T > and the use of aclass < out bclass > are equivalent to Definition of class aclass < out t > and use of aclass < bclass >.

Due to the covariance and inversion of out and in, Kotlin will make some restrictions on the class when it appears in the definition:

  • out's generic class does not allow T as a parameter to a method unless annotated with @ UnsafeVariance. (only get, not set)
  • The generic class of in does not allow T as a return type. (only set, not get)

Vi. code example

6.1 when out and in are in use
class ChangeKotlin {
    open class A
    open class B : A()
    open class C : B()

    class AClass<T> {
        private var mT: T? = null
        fun get(): T? {
            return mT
        }

        fun set(t: T) {
            mT = t
        }
    }

    fun <T> d(list: List<T>) {

    }

    fun main() {
        val aClass: AClass<String> = AClass()
        var aClass1: AClass<Any> = AClass()
        aClass1 = aClass // Report errors

        val aClassA: AClass<A> = AClass()
        val aClassB: AClass<B> = AClass()
        val aClassC: AClass<C> = AClass()

        val aClassExtendB1: AClass<out B> = aClassA // Report errors
        val aClassExtendB2: AClass<out B> = aClassB
        val aClassExtendB3: AClass<out B> = aClassC
        val b2: B? = aClassExtendB2.get() // Can get to B
        aClassExtendB2.set() // Error will be reported if any value is set.

        val aClassSuperB1: AClass<in B> = aClassA
        val aClassSuperB2: AClass<in B> = aClassB
        val aClassSuperB3: AClass<in B> = aClassC // Report errors
        val o: Any? = aClassSuperB1.get() // Can only get Any
        aClassSuperB1.set(A()) // Error will be reported for types other than B and its subclasses.
        aClassSuperB1.set(B())
        aClassSuperB1.set(C())
    }
}

6.2 when out and in are in the definition

class ChangeKotlin2 {
    open class A
    open class B : A()
    open class C : B()

    class AClassOut<out T> {
        private var mT: T? = null
        fun get(): T? {
            return mT
        }

        // Error is reported. T is not allowed as a parameter unless the @ UnsafeVariance annotation is added.
        //fun set(t: T) {
        //    mT = t
        //}
    }

    class AClassIn<in T> {
        private var mT: T? = null
        // Error will be reported when writing t as return type. T is not allowed as return type.
        fun get(): Any? {
            return mT
        }

        fun set(t: T) {
            mT = t
        }
    }

    fun main() {
        val aClass: AClassOut<String> = AClassOut()
        var aClass1: AClassOut<Any> = AClassOut()
        aClass1 = aClass // normal

        val aClassAOut: AClassOut<A> = AClassOut()
        val aClassBOut: AClassOut<B> = AClassOut()
        val aClassCOut: AClassOut<C> = AClassOut()

        val aClassExtendB1: AClassOut<B> = aClassAOut // Report errors
        val aClassExtendB2: AClassOut<B> = aClassBOut
        val aClassExtendB3: AClassOut<B> = aClassCOut
        val b2: B? = aClassExtendB2.get() // Can get to B
        aClassExtendB3.set(B()) // Error reporting. During the definition period, there is no such method.

        val aClassAIn: AClassIn<A> = AClassIn()
        val aClassBIn: AClassIn<B> = AClassIn()
        val aClassCIn: AClassIn<C> = AClassIn()

        val aClassSuperB1: AClassIn<B> = aClassAIn
        val aClassSuperB2: AClassIn<B> = aClassBIn
        val aClassSuperB3: AClassIn<B> = aClassCIn // Report errors
        val o: Any? = aClassSuperB1.get() // Can only get Any
        aClassSuperB1.set(A()) // Error will be reported for types other than B and its subclasses.
        aClassSuperB1.set(B())
        aClassSuperB1.set(C())
    }
}

Last

We are just a small part of the long road of development... Only continuous learning and advancement is our way out! To keep up with the progress of the times! At the beginning of this year, I spent a month to collect and sort out a set of knowledge system. If you have an idea to study deeply and systematically, you can click Portal , I will give you all my collected and sorted materials to help you advance faster.

Posted by curtm on Mon, 28 Oct 2019 20:56:20 -0700