当前位置:网站首页>Sorting out Android kotlin generic knowledge points

Sorting out Android kotlin generic knowledge points

2022-06-21 17:30:00 Lighthouse @kuaidao

Preface

Learning knowledge requires setting goals in advance , Only by learning with problems can we have a definite aim . Whether it's java Generic or kotlin Language generics are all writing frameworks , Write general tool artifact . If you are not familiar with generic Syntax , There will be many strange problems in the development process . Of course, the advanced features of the language must not be understood .

Ben blog be based on 《kotlin actual combat 》 Chapter 9 comes from the understanding of generics

1. Declaration of generic functions and classes

kotlin Introducing new concepts : Materialization type parameter Declare point variants Use point deformation

Materialization type parameter : Type parameter modification of generic function refixed Embellishment Such as : , And set the generic function to inline Inline function , Then the specific types of generic arguments of generic parameters can be obtained at runtime .( Ordinary classes and functions don't work , Not inline The runtime type information of function arguments will be erased )

Declare point deformation : You can tell whether a generic type with type parameters is a subtype of another generic type , Their basic types are the same , The type parameters are different

// Local variants of the declaration 
interface Compare< A>{
     }
interface Compare< B>{
     }

Use point deformation : Can reach and java wildcard ( ?) Same effect

interface Compare{
     
// Use point variant 
 fun  <T> compare(o1:T,o2:T){
     }
}

1.1 Generic Type Parameter ( Generic classes )

class A<T>{
    

}
class B :A<String>()

Above : Two classes A、B, class A Followed by... In angle brackets T, Called a class A Of Type parameter or type parameter , And category B Derived from class A, And for classes A The generic parameters of Materialization , class B Followed by the class A In angle brackets of String type , Become Type arguments , It can also be said as a class B Middle to class A The type parameter of the uses String Type is materialized . It can be analogous to the initial assignment of parameters .

Of course, a class can declare more than one type parameter , You can also state N individual , For example kotlin Of Functions.kt The statement 22 There are so many :

//Functions.kt class 
/** A function that takes 22 arguments. */
public interface Function22<in P1, in P2, in P3, in P4, in P5, in P6, in P7, in P8, in P9, in P10, in P11, in P12, in P13, in P14, in P15, in P16, in P17, in P18, in P19, in P20, in P21, in P22, out R> : Function<R> {
    
    /** Invokes the function with the specified arguments. */
    public operator fun invoke(p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7, p8: P8, p9: P9, p10: P10, p11: P11, p12: P12, p13: P13, p14: P14, p15: P15, p16: P16, p17: P17, p18: P18, p19: P19, p20: P20, p21: P21, p22: P22): R
}

tips: java The language allows the use of generic types without type parameters ( Original ecological type ) Because java 1.5 To introduce generics , It needs to be compatible with the old version , and kotlin Native types are not supported , Type parameters must be defined , because kotlin Support generic types from the beginning .

    //①
     ArrayList list=new ArrayList();
   //②
    val list: ArrayList<*> = ArrayList<Any?>()

1、2 Represent the java Primitive types , Without type parameters ,②kotlin The equivalent of language must define type parameters

1.2 Generic functions and properties

fun <T> List<T>.slice(indices:IntRange):List<T>

class A<T>(val a: T) {
    
    
}

explain : Type parameter declaration , The receiver and return value use type parameters T. Generic types must be defined , If the system can deduce it, there is no need to specify the generic type manually . such as

val letter =('a' ..'z').toList()
// There are two ways to write it. One is that the generic argument type is not specified , One is to specify generic arguments , This is equivalent to ,
// Because the second system can pass through letters The values stored in the set are derived T The value of is Char.
println(letters.slice<Char>(0..2))
println(letters.slice(0..2))

1.3 Declare generic classes

kotlin By adding a pair of angle brackets after the class name , And put the type parameter in angle brackets to declare the generic class or
Generic interface

class A<T>{
    }

interface List<T>{
    
operator fun get(index:Int):T
}

Inside the interface T It can be used as a common type , If your class inherits a generic class , Then you need to use generic arguments to materialize generic parameters . Type arguments can be concrete types or Another type parameter

class ArrayList<T> :List<T>{
    
 override fun get(index:Int):T = ...
}

Notice the ArrayList Type parameter of T, and List Medium T Is not a T, Names can all be called T, Or not T. Call others abc,Ac Fine

class ArrayList<B> :List<T>{
    
 override fun get(index:Int):T = ...
}

1.4 Type parameter constraint

Why do I need to be right Generic Type Parameter Make constraints , You can restrict the types of type arguments that are generic classes and generic functions , Limit List Only subtypes derived from a type or called itself... Can be added to the collection Upper bound constraint , Restricting the collection to only add certain types of superclasses is called Lower bound constraint , Here we can combine java Declaration of generics :<? super T> and <? extends T>

java How to write it :

<T extends Number> T sum(List<T> list)

<T super Number> T sum(List<T> list)

kotlin How to write extension functions :

fun<T:Number> List<T>.sum():T

In rare cases, you need to specify multiple constraints on a type parameter , It can be used if necessary where keyword ,

fun<T> ensureTrailingPeriod(seq:T):where T:CharSequence,T:Appendable{
    

}

In this case, the parameter types as type arguments must be implemented by colleagues CharSequence and Appendable Two interfaces .

1.5 Make the type parameter non empty

By default, generic types T The type is Any?, That is, generic types can be assigned with null and non null values . So how to restrict the type parameter to be non null , You only need to explicitly make a non empty constraint on the generic parameter to achieve .

// The default type is T:Any?
class Processor<T:Any>{
    
	fun test(value:T){
    
		value.hashCode()
	}
}

2. Instantiate type parameters and type erasure

jvm Upper generics are generally implemented by type erasure , The type arguments of generic type instances are in The runtime is not reserved .

2.1 Type checking and conversion

kotlin The generics of are also erased at runtime , This means that a generic class instance does not carry information about the type arguments used to create it .

val list=listof(1,2,3)
val strList=listof("a","b","c")

At run time list、 and strList You don't know if they are declared as strings , Integer list or other object list . Because the runtime does not attach any type of argument information . The advantage of this is to save memory , Less type information is stored in memory .

So use is List It can't be . But it can. Asterisk projection To determine whether the type is a List List instead of Set list .<*> similar Java Medium <?> Indicates that you have an unknown type .

fun printSum(c:Collection<*>){
    
    val intList=c as? List<Int>?:throw IllegalArgumentException("List is expected")
    println(intList.sum())
}

fun main(args: Array<String>) {
    
    printSum(listOf(1,2,3))
    printSum(listOf("1","2","3"))
}

This function is used to count the elements of a set , Normal output for the first case 6, And the second case is right String List summation of types , You will be prompted with a type conversion exception

Exception in thread "main" java.lang.ClassCastException: class java.lang.String cannot be cast to class java.lang.Number 

How to avoid this abnormal situation ?
kotlin There are special syntax structures that allow you to use specific type arguments in the body of a function , But only inline A function can

2.2 Declare functions that implement type parameters

inline fun <reified T> isA(value:Any)=value is T

Above isA The function is declared as inline Function with generic arguments reified modification .(reified Materialize modifier ),reified Declared that type parameters will not be erased at run time .

public inline fun <reified R> Iterable<*>.filterIsInstance(): List<@kotlin.internal.NoInfer R> {
    
    return filterIsInstanceTo(ArrayList<R>())
}

fun main(args: Array<String>) {
    
    println(listOf(1,2,"abc").filterIsInstance<String>())
// printSum(listOf("1","2","3"))
}


System level materialization api, Traverse the elements in the list , Determine whether the element is an object of the specified type .

Why is materialization only valid for inline functions ?

Because the bytecode code of the inline function will be compiled into the calling function , Every time you call a function that implements a type parameter , The compiler knows the exact type used as a type argument in this particular call . When called filterIsInstance< String>(), Type arguments passed . Because the generated bytecode refers to a specific class , Instead of type parameters , It will not be affected by type parameter erasure at runtime .

tips:
belt reified Of type parameter inline Function cannot be in java Call in code , Ordinary inlining can be done in the same way as regular functions java Call in , But the inlining feature is lost . Functions with typed materialized arguments require special handling , Replace the value of the type argument with the bytecode . Must always be inline , So it can't be used java Call in the normal way .

2.3 Materialized types replace class references and restrictions on their use

Materialization reified Use scenarios .

inline fun <reified T:Activity> Context.startActivity(){
    
	val intent=Intent(this,T::class.java)
	startActivity(intent)
}

Materialization type T, It can be used as a concrete type in a function

Can be used in :

  1. Type checking and type conversion (is,as?)
  2. Use kotlin Reflection of api
  3. obtain Class(::class.java)
  4. As type arguments for calling other functions

Can't do :

  1. Create an instance of a class that specifies type parameters
  2. Call the companion object of the type parameter
  3. When calling the parameter function of the materialized type, use the formal parameter of the non materialized type as the type argument
  4. Ba class , The type parameter of an attribute or non inline function is marked as reified

3. Variant : Declare point deformations and use point deformations

Variant : Used to describe arguments with the same base type and different types ( Generic ) How the types of are related . for example List<String> and List<Any>, A proper understanding of deformation helps to create that which is neither inconvenient nor Restrict users , It will not destroy what users expect Type safety

 
 fun printContents(list:List<Any>){
    
 	println(list.joinToString())
}

fun main{
    
	printContents(listof("a","b"))
}

>>>>  a,b

The function treats each element as Any treat , Because the string is Any, It's completely safe . Then there is a situation to consider , If the formal parameter is List Will function interface types still be safe ?

 fun printContents(list:MutiableList<Any>){
    
 	list.add(100)
}

This is the unsafe type , Because in the function for List The element of . If you call in the above way, you will report a type conversion exception .

3.1 class 、 type 、 subtypes

The type of the variable specifies the possible values of the variable , Sometimes you can think of types and classes as the same concept . But they are not exactly the same .

A generic class : You can use the class name as a type directly .

var x:String
var x:String?

 One kotlin Class can be used to construct at least two types 【 Null type 】 and 【 Non empty type 】

The case of generic classes becomes more complicated , To get a valid type, you need a type argument to replace the type parameter .List Not a type , But all the alternatives listed below are legal types .List、List<List>, Each generic class may generate a potentially wireless number of types .

subtypes 、 Supertype It's the opposite . In a specific context . Whenever you need a type A Value , Can use types B The value of . type B It's the type A Subtypes of . And vice versa A It's the type B The supertype of . The compiler checks this every time it assigns a value to a variable or passes an argument to a function ( Whether the type of the argument is a subtype of the parameter type .)

subtypes and Subclass , It essentially means the same thing , however kotlin Nullable and non nullable types of the same class , Do not follow this .A yes A? Subtypes of . The reverse is not true .

Invariant type : A generic class MutableList If for any two types A and B,MutableList neither MutableList Its subtype is not its supertype ,MutableList It is called immutable on this type parameter . stay kotlin Speaking in a language environment ,java Classes in are immutable .( Readable class ,Mutiable Modify the read / write class )

3.2 Covariance , Inversion

Covariance : Keep subtype relationships , Modifier :<out T> Can only be used in the return value position
Inversion : Reverse rotor type relation , Modifier <in T>, It can only be used in the input position .

interface TransFormer<out T>{
    
	fun transform(t:T):T
}

Here is the parameter declaration t:T yes in Location , Return value T yes out Location , Type parameter T Keywords on out There are two meanings .

  1. Subtypes will be preserved (Producer<Cat> yes Producer<Animal> Subtypes of )
  2. T Can only be used in out Location

for example :Kotlin type List The declaration of leads to List Is a readable list , Not right List Conduct add Elements .

// System declaration  List<out T> Retaining subtypes will be preserved 
public interface List<out E> : Collection<E> {
    

}
//
interface Comparator<in T>{
    
  fun compare(e1:T,e2:T):Int{
    }
}
Covariance Inversion Immutable
Producer< out T>Consumer< in T>MutableList< T>
The subtype relationship of the class preserves :Producer< Cat> yes Producer< Animal> Subtypes of Subtypes are reversed ,Consumer< Animal> yes Consumer< Cat> Subtypes of No subtypes
T Only in out Location T Only in ini Location T Can be in in perhaps out Location or any other location

Tips:
kotlin Representation (P)->R Is said Function<P,R> Another form of readability , It can be written.
<in P,out R>, Means the first type parameter of this function type , Subtypes are reversed , The second type parameter subtype preserves .

3.3 Use point variant : Specify the variant where the type appears .

Declare point variants : It is convenient to specify parameter types and use deformation modifiers when declaring classes . After the modifier is decorated , Will be applied where all classes are used .
Use point deformation : Every time you use a type with a type parameter , You can specify whether type accommodation can be replaced with its subtype or supertype . such as java Wildcard syntax for <? extends&gt,<?Super>;

kotlin Point deformation is also supported , Allows you to specify deformations where type parameters appear .( Even if the type declaration cannot be declared as Covariance or Inversion

MutableList Neither covariant nor contravariant , Because it produces and consumes values of the types specified as their type parameters at the same time , But the common scenario for this type of variable is , It is common to use a particular function as a role .

fun <T> copyData(source:Mutiable<T>,destination:MutableList<T>){
    
	for(src in source){
    
	  destination.add(src)
	}
}

 To make the function support different types of lists , A second generic parameter can be introduced 

fun <T:R,R> copyData(source:Mutiable<T>,destination:MutableList<R>){
    
	for(src in source){
    
	  destination.add(src)
	}
}

 You can use the morph modifier to achieve the same function more gracefully 

fun <T> copyData(source:Mutiable<out T>,destination:MutableList<int T>){
    
	for(src in source){
    
	  destination.add(src)
	}
}

tips:
kotlin Use point variant to directly correspond to java Bounded wildcard ,kotlin Medium MutableList<outT> Corresponding java Medium MutableList<? extends T> It means the same thing ,in Projective MutableList<in T> Corresponding java Of MutableList<? super T>, Using point variants helps to broaden the range of acceptable types .

3.4 Model projection : Use * Instead of type parameters

Asterisk projection : Indicates that you do not know any information about generic arguments

MutiableList<> And MutiableList<Any?> Dissimilarity ,(MutiableList stay T Is of invariant type ),MutiableList<Any?> Can contain any type of element ,MutiableList<> Contains elements of a specific type , The specific element doesn't need to be concerned .

MutiableList<*> Contains a list of special types , The specific type is not clear , It leads to a problem , This type of collection can only read and cannot write any elements ( I don't know which type can be stored , An error will be reported for any type of deposit )

summary :

kotlin Generics and java Quite close to , Declare generic classes and generic functions in the same way ,kotlin Generic , Type arguments are also erased at run time , So it can't be used is Operator , You can declare a function as inline , The type parameter is marked as reified After implementation, the type arguments of generic parameters can be obtained at function runtime for use is Judge . Variant is a way to describe the subtype relationship between generic classes with different type parameters of the same base class . It states that one of the generic parameters is a subtype of the other generic parameter , The reverse is the supertype . You can declare that a class is covariant on a type parameter , If this parameter is only used in out Location , Inversion is just the opposite , Only in in Location ,kotlin in List Declared covariant , that List< String> yes List< Any> Subtypes of .MutiableList Function type , You can declare inversion on the first parameter , The second parameter is covariant . send (Animal)->Int Become (Cat)->Number Subtypes of .kotlin You can specify variants for the entire generic class ( Declare point deformation ), You can also specify variants for the specific use of generic types ( Use point variant ); When the exact type arguments are unknown or unimportant , You can use projection syntax

原网站

版权声明
本文为[Lighthouse @kuaidao]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/172/202206211518336481.html