Kotlin detailed article notes collation update progress: Kotlin Series – High order functions and Common functions in standard Library (3) Kotlin Series – Advanced Deep Generic Covariant Inversion from Java to Kotlin(4) Koltin series – Coroutines from Recognition to Use in Android (5)

preface

This article will start with Java generics, then switch to Kotlin, focusing on Java generics grasp, Koltin’s generics will soon grasp. (You can choose your own section to eat, code word is not easy to read and feel ok, please give a thumbs-up, MY ability is limited, there are mistakes or questions in the comment section, thank you ~~)

conclusion

  • Virtual machines have no generics, only common methods and classes.
  • All type parameters are replaced with their qualified types.
  • The bridge method is synthesized to preserve polymorphism.
  • To preserve type safety, insert casts if necessary.

First, generic base

Definition 1.

Generic types, also known as parameterized types. Multiple types can be applied to code. Use type parameters, enclosed in Angle brackets, after the class name. Replace the type parameter with the actual type when using the class. Example:

ClassA<T>{}Copy the code

2. The meaning of existence

If you can only use concrete types, concrete primitive types, or custom classes in your program, and you are writing multiple types of code, this restriction can be very restrictive. We need a way to determine the type before it can be run to make the code more generic. Example:

public class PrintClass {
        static  public void printInt(Integer a, Integer b) {
            System.out.println("Parameter a =" + a + "Parameter b =" + b);
        }

        static   public void printFloat(Float a, Float b) {
            System.out.println("Parameter a =" + a + "Parameter b =" + b);
        }

        static   public void printDouble(Double a, Double b) {
            System.out.println("Parameter a =" + a + "Parameter b ="+ b); }}Copy the code

Change to a generic function:

public class PrintClass1 {
        static  public <T> void printMultiply(T a, T b) {
            System.out.println("Parameter a =" + a + "Parameter b ="+ b); }}Copy the code

Use:

Public static void main(String[] args) {printclass.printDouble (10.0,10.0); PrintClass. PrintInt (10, 10); PrintClass.printFloat(10f,10f); PrintClass1. PrintMultiply (10.0, 10.0); PrintClass1. PrintMultiply (10, 10); PrintClass1.printMultiply(10f,10f); PrintClass1.printMultiply("100"."100"); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- print Log -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- parameters a = 10.0 b = 10.0 parameters a = 10 b = 10 parameters a = 10.0 b = 10.0 Parameter A =10.0 Parameter B =10.0 Parameter A =10 Parameter B =10 Parameter A =10.0 parameter B =100 Parameter B =100Copy the code

From the above, you have a basic understanding of generics.

The use of generics in Java

Interface generics

  • Interface generic definitions:
interface Animal<T> {
    void name();
    void cry();
    void mysteryData(T t);
}
Copy the code
  • Interface generic implementation one
public class Cat implements Animal<String> {
    @Override
    public void name() {
        System.out.println("Cat");
    }
    @Override
    public void cry() {
        System.out.println("Meow");
    }
    @Override
    public void mysteryData(String s) {
        System.out.println("Suppose it has a data type."+ s.getClass().getName()); }}Copy the code
  • Interface generic implementation two
public class Dog<T> implements Animal<T> {
    @Override
    public void name() {
        System.out.println("Dog");
    }
    @Override
    public void cry() {
        System.out.println(Woof woof woof);
    }
    @Override
    public void mysteryData(T t) {
        System.out.println("Suppose it has a data type."+t.getClass().getName()); }}Copy the code

Use:

    public static void main(String[] args) {
        Dog<Integer> dog = new Dog();
        dog.name();
        dog.cry();
        dog.mysteryData(10);

        Cat cat =new Cat();
        dog.name();
        cat.cry();
        cat.mysteryData("String"); } -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- the log log dog auf Assuming that it has a data type Java. Lang, Integer dog meow meow assuming that it has a data type Java. Lang. StringCopy the code

2. The generic class

The above interface generics implementation 2 uses the implementation of class generics.

Method generics

public class PrintClass1 {
        static  public <T> void printMultiply(T a, T b) {
            System.out.println("Parameter a =" + a + "Parameter b ="+ b); }}Copy the code

Generic type variables are qualified

Sometimes classes and methods need to constrain type variables.

For example: Add a class that prints the sounds of various animals.

class AnimalCry{ public static <T> void cry(T a){ a.cry(); }}Copy the code

Here you simply write like this, it will not recognize the method cry, because this method is held by Cat, Dog and other methods of Animal interface, so we need to give it a type variable limit.

class AnimalCry{ public static <T extends Animal> void cry(T a){ a.cry(); }} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- call public static void main (String [] args) {AnimalCry. Cry (new Dog ()); } -------------------- printed Log woof woof woofCopy the code

The

extends can be followed by an interface or class name. T: represents a subtype of the binding type. Multiple qualified types qualify multiple types with &, such as

Four, generic type wipe

There are no generic type objects in Java virtual machines. All objects are ordinary classes. Whenever a generic type is defined, a corresponding primitive type is automatically used. The name of the original type is the type name of the wiped type parameter. If there is no qualified type variable, Object is used instead, and if there is a finite rule, qualified is used instead.

public class PrintClass1 {
        public static  <T> void printMultiply(T a, T b) {
            System.out.println("Parameter a =" + a + "Parameter b ="+ b); }} -------------- compiled for public class PrintClass1 {public static voidprintMultiply(Object a, Object  b) {
            System.out.println("Parameter a =" + a + "Parameter b ="+ b); }}Copy the code
class AnimalCry{ public static <T extends Animal> void cry(T a){ a.cry(); Public static void cry(Animal a){a.clay (); }}Copy the code

5. Constraints and limitations

Most of the limitations are imposed by type wipes.

  1. Type parameters cannot be instantiated with primitive types.There is noPair<double>onlyPair<Double>, because after the generic wipe,PairClass containsObjectType field, andObjectCannot storedoubleValue.
  2. Runtime type queries are only available for primitive virtual machine objects that are always of a non-generic type. Therefore, all type queries produce only primitive types.
Pair p = new Pair("str"."str1");
Pair i = new Pair(10,20);
// illegal generic type forInstanceof The instanceof keyword cannot be used to determine the type of a generic classif(p instanceof Pair<String,String>trueBecause both calls return pair.classif (p.getClass() == i.getClass()){} 

Copy the code
  1. Cannot create an array of parameterized types
Pair<String,String>[] pairs = new Pair[10]; Pairs [0] = new Pair (10, 20). // Can be assigned but will result in a type error.Copy the code
  1. You cannot instantiate a type variable or instantiate a generic arrayCannot use similarnew T(...),new T[],T.classAnd so on
  2. The static context type variable of the generic class is invalid
public class Singleton<T>{
      private static T singleInstance;  //ERROR

      public static T getSingleInstance(){ //ERROR
          if(singleInstance == null) 
              returnsingleInstance; }} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- - type erasure is substituted into the Object after the concrete class public class Singleton {private static Object singleInstance;  public static ObejctgetSingleInstance() {if(singleInstance == null) 
              returnsingleInstance; }} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- call wrong, return the Object type AType a = Singleton. GetSingleInstance (); Error, this usage is not allowed and only the generic argument AType a = Singleton<AType>.getSingleInstance() is passed when a method or constructor is called; -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- but if it is a public static < T > TgetSingleInstance(){//YES Static methods can declare generic parametersif(singleInstance == null) 
              return singleInstance;
      }
Copy the code
  1. You cannot inherit exceptions or Throwable. You cannot throw or catch instances of generic classes, but you can throw generic-qualified exceptions
Public class Pair<T,Q> extends Exception{} public static <T extends Throwable> voiddoWork(Class<T> t){ try{ .... }catch(T e){// the generic type cannot be thrown here}} // correct. public static <T extends Throwable> voiddoWork(T t) throws T{
  try{
  ....
  }catch(Throwable t){ 
  }
}

Copy the code

Generic type inheritance rules

  1. The two generic parameters are inherited, but the corresponding generics are not related at all!
interface Animal{} public class Cat extends Animal{} public class Dog extends Animal{} public class Cry<T>{} -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- Cry < Animal > and Cry > < Cat is not inheritance, also does not have what relation.Copy the code
  1. A generic class can extend or implement other generic classes
public class ArrayList<E> extends AbstractList<E>
Copy the code

7, wildcard type (key!!)

Summary:

Covariance:
specifies the upper limit of a generic type. It can only be read and cannot be modified. (Modifying means adding elements to a generic collection, such as remove(int index) and clear.)

Inverter:
specifies the line of the generic type, which can only be modified but cannot be read.

<? >The equivalent of<? extends Object>Specify generic types with no restrictions

The picture above is an example:

1. The covariance:<? extends Class>Specifies the upper limit of a generic type

Its scope is limited to the upper bound itself (the upper bound can be an interface) and all direct and indirect subclasses.

  • Application scenarios
Animal animal = new Dog(); // Java polymorphic List<Dog> dogs = new ArrayList<Dog>(); List<Animal> animals = dogs; Incompatible types: List<Dog> cannot be converted to List<Animal>Copy the code

The above example does not allow this assignment to be type safe because of type wiping. You can use the covariant
limits the upper bound of the parameter type, that is, the generic type, which must satisfy this extends restriction.

List<? extends Animal> animals = new ArrayList<Animal>(); A List < / / itself? extends Animal> animals = new ArrayList<Cat>(); // Subclass List<? extends Animal> animals = new ArrayList<ShamoDog>(); // Indirect subclassCopy the code
  • limit

Can only provide external data to be consumed, similar to producers.

List<? extends Animal> animals = new ArrayList<Dog>(); Animal animal= animals.get(0); // Get Animal animals.add(textView); // No suitable method foundfor add(TextView)
Copy the code

2. Invert: <? Super Class> specifies the lower limit for generic types

Its scope is limited to the lower bounds themselves (which can be interfaces) and all direct and indirect parent classes.

  • Application scenarios
List<? super ShamoDog> shamoDogs= new ArrayList<shamoDogs>(); A List < / / itself? super ShamoDog> shamoDogs= new ArrayList<WailaiDog>(); List<? super ShamoDog> shamoDogs= new ArrayList<Animal>(); // Indirect parent classCopy the code
  • limit

Only Object objects can be read, and usually only used to add data, i.e. consuming existing List
, to which Dog is added, so this generic type declaration relative covariant can be called a consumer

List<? super ShamoDog> shamoDogs = new ArrayList<Animal>(); Object object = shamoDogs.get(0); // Get Object Dog Dog =... shamoDogs.add(dog); // Add is okCopy the code

Kotlin’s generics

1. The format

The same format as Java generics

Fun <T>TestLooperManager(T :T): Unit {}// Method genericCopy the code

2. Key wordsout,in,*,where

  • out: covariant, andjavaThe upper limit wildcard of<? extends BoundType>The corresponding
  • inContravariant, andjavaThe lower bound wildcard of<? super BoundType>The corresponding
  • *And:javathe<? >, butjavais<? extends Object>.kotlinis<out Any>
  • whereAnd:javathe<T extends Animal & Person >the&Symbols correspond
//wherePublic class Cry<T extends Animal & Person>{} public class Cry<T extends Animal & Person>where T:Animal,T:Person{ }
Copy the code

Important: Kotlin provides an additional capability to declare a class by adding the in keyword to the generic type, indicating that the generic parameter T will only be used as input, and not as an additional in. For out, it indicates that the generic parameter T will only be used for output.

example

// Koltin's List public interface List<out E> : Collection<E> {} var animalKot:List<Animal<String>> = ArrayList< String>>( Animal<String>> = ArrayList< String>inDog<String>> = ArrayList<Animal<String>>(in// Define oneinGeneric type class All<inT>{fun p(T :T){}} var all: all <String>> = all ()inDog<String>> = All()// writeinVar all: all <out Dog<String>> = all (in

Copy the code

3. The key wordreified

In the case of wiping Java generics, the run-time type queries mentioned in point 2 of 5 constraints and Limitations above apply only to primitive types

<T> void println(Object obj) {
    if(obj instanceof T) {// IDE error, illegal generictype forInstanceof}} kotlin, too -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- fun < T > println (any: any) {if(any is T) {// IDE error, Cannot checkfor instance of erased type: T
    }
}
Copy the code

Java workaround: Pass an extra argument of type Class

and check it with the Class#isInstance method

<T> void println(Object obj, Class<T> type) {
    if (type.isInstance(obj )) { 
    }
}
Copy the code

Kotlin’s solution to this problem is the reified keyword, but reified can only be used in inline methods, so use inline methods.

inline fun <reified T> println(any: Any) {
    if (any is T) {
        println(item)
    }
}
Copy the code

Inline functions that disassemble method calls into statement calls when methods are compiled to reduce unnecessary object creation. In Kotlin an inline can be reified, which means we can get a Class that uses a generic type.

  • Custom extension Gson used in the project
Inline fun <reified T> gson.fromjson (json:String):T = fromJson(json,T::class.java)Copy the code

4. Note@UnsafeVariance

public interface List<out E> : Collection<E> {
    // Query Operations

    override val size: Int
    override fun isEmpty(): Boolean
    override fun contains(element: @UnsafeVariance E): Boolean
    override fun iterator(): Iterator<E>

    // Bulk Operations
    override fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
    ....
Copy the code

Override fun contains(Element: @unsafevariance): override fun contains(element: @unsafevariance): override fun contains(element: @unsafevariance): Boolean To support input, use @unsafevariance to avoid IDE checks

5. Where Kotlin generics differ from Java generics

  • Arrays in Java support covariance, whereas arrays in Kotlin do not. Arrays in Kotlin are represented by the Array class, which uses generics just like collections, and therefore does not support covariance.

  • The List interface in Java does not support covariance, whereas the List interface in Kotlin does. In Kotlin, the MutableList interface is actually the Java equivalent of a List. The List interface in Kotlin implements read-only operations and no writes, so there are no type-safety issues and covariance is naturally supported.

Thank you

Kotlin’s Generic Java Core Technology Volume 1