Get to know Kotlin thoroughly

origin

In 2019, Google announced at I/O that the Kotlin programming language is now the language of choice for Android application developers. (Kotlin-first)

Kotlin, developed by JetBrains, is 100% Java interoperable and has many features that Java does not support. Two years ago, at I/O 2017, Google announced support for Kotlin in its Android Studio IDE. Kotlin began to co-exist with Java as a first-level language for Android development. Because Java has contributed so much to the evolution of Android, the idea that it is the language of choice for Android is firmly established, and it is improving all the time.

Within a few years, However, Kotlin quickly took over the market due to its advantages over Java, such as greater security and simplicity, said Chet Haase, Google’s chief Evangelist for Android: “Kotlin’s popularity has been growing over the past two years, with over 50% of professional Android developers now using Kotlin to develop their apps.” This can be seen in the data from this year’s Stack Overflow annual Developer Survey, which shows that Kotlin received 72.6% positive feedback among developers’ favorite programming languages.

Why did Google choose Kotlin

The most immediate reason Google chose Kotlin was because of Oracle’s rivalry.

Oracle bought Sun, which developed Java in 1995, for $7.4 billion in 2010 and sued Google for $8.8 billion in less than eight months for copyright infringement on the Java language.

A look back at oracle and Google’s Java copyright tug-of-war:

  • In 2010, Oracle sued Google, claiming that Google’s Android operating system made unauthorized use of Java apis
  • In 2012, Google successfully convinced the court that API was not protected by copyright, and a district court ruled that API was not protected by law and dismissed the case
  • In 2012, Oracle complained about the ruling and appealed to the United States Court of Appeals
  • In 2014, three appellate court judges unanimously threw out the district court’s decision in the case and declared API protected by copyright
  • In 2014, Google appealed the ruling to the Supreme Court
  • In 2015, the Supreme Court rejected Google’s appeal and sent the case back to a district court
  • In March 2016, Oracle raised the claim to $9.3 billion
  • In April 2016, a settlement meeting between Google’s CEO and Oracle’s failed
  • In May 2016, the San Francisco District Court held that Google’s use of Java apis was protected by the “Fair Use” principle
  • Oracle appealed to the Court of Appeals for the Federal Circuit in October 2016
  • In 2017, the Court of Appeals for the Federal Circuit heard Oracle’s appeal
  • In March 2018, the Court of Appeals for the Federal Circuit ruled in Favor of Oracle
  • Google then appealed again, but it was dismissed in August 2018
  • In January 2019, Google asked the U.S. Supreme Court for a final ruling on its Java API copyright lawsuit with Oracle

Google chose Kotlin simply to get out of patent litigation. Kotlin, of course, does his part. Excellent performance indeed.

A glance at grammar

1.Hello World

    @JvmStatic
    fun main(args: Array<String>) {
       print("Hello World!")}Copy the code

2. The variable

First we define a num variable with var and a STR variable with val.

var num : Int = 1
val str : String = "test"
Copy the code

According to the figure above, two points need to be noted:

2.1. Val is equal to the final

We can see that num defined by var can be reassigned, but STR cannot. Val is actually equivalent to final String in Java, meaning that variables defined by val have the final keyword added by default.

2.2. Can be empty? And empty matching

The second point is that the num variable cannot be assigned to Null if it is defined as Int, and we need to do so if necessary

var num : Int?
Copy the code

2.3. Type inference

val str = ""
Copy the code

Kotlin has type inference and the above statement is equal to Java

final String str = ""
Copy the code

Custom getter&setter

 var msg: String
    get() = field
    set(value) {
        field = value
    }
Copy the code

3. The function

Definition 3.1.

3.1.1 Ordinary functions

Define a function called test that returns a String? , may return null

  fun test(a): String? {
        return null
    }
Copy the code

Call:

The call is similar to Java, since test returns a nullable string, add? : indicates that the current face is empty.

valresult = test() ? :"x"
Copy the code

Can also be

fun isEmpty(str: String) = str.isEmpty()
Copy the code

IsEmpty returns the value of isEmpty().

3.1.2 Default Parameters

Kotlin supports functions with default parameters, which are default if not passed.

data class EnvConfig(val baseUrl: String, val isDebug: Boolean = false)

// Construct 1 equals EnvConfig("https://xx.com",false)
val env1 = EnvConfig("https://xx.com")
  
Copy the code
3.1.3 Named Parameters

The Kotlin method call can specify parameter names to avoid confusion. It’s more intuitive.

EnvConfig(
      baseUrl = "https://xx2.com",
      isDebug = true
   )
Copy the code

3.2 Top-level functions and attributes

Kotlin can define globally callable utility functions, which are compiled into static methods of the file to be called.

TopFunc.kt

fun toString(obj:Any) = obj.toString()
Copy the code

Translate to Java classes

public final class TopFuncKt {
   @NotNull
   public static final String toString(@NotNull Object obj) {
      Intrinsics.checkParameterIsNotNull(obj, "obj");
      returnobj.toString(); }}Copy the code

Same thing with the top-level property

var count = 0
Copy the code

Java

    public static final int getCount(a) {
      return count;
   }

   public static final void setCount(int var0) {
      count = var0;
   }
Copy the code

3.3 Add methods to someone else’s class: extend functions and attributes

3.3.1 Extend the function

An extension function is very simple; it is a member function of a class.

TopFunc.kt

// Define a member function method in which this refers to the extended object.
// where this is the string
fun String.print(a) = println(this)

/ / use
"string extension".print()

/ / output
string extension
Copy the code

The extension function is also a top-level function, so it is also a static function in Java, called as follows:

TopFuncKt.print("extension in Java");
Copy the code

Extension functions exist only to shorten the syntax. They are not “extensions” in the true sense of the word, nor can they be really extended, so extension functions cannot be overwritten or called as member functions in Java.

3.3.2 Extend attributes

Similar to extension functions, extended properties provide a way to extend a class’s API to access properties using property syntax instead of function syntax. ** Although they are called attributes, they can have no state and no proper place to store them. ** You cannot add additional fields to an existing instance of a Java object.

var StringBuilder.lastChar: Char
    get() = get(length - 1)
    set(value: Char) {
        setCharAt(length - 1, value)
    }
Copy the code

** The code above provides a quick way to access the class’s member methods using the extended attribute, but does not add the lastChar attribute to the StringBuilder class. ** However, extended attributes are still useful, such as converting a common Android Float to DP:

val Float.dp
    get() =  get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this, Resources.getSystem().displayMetrics)
Copy the code

3.4 Variable arguments and infix calls

Vararg: variable parameter. Just be careful

fun test(vararg string: String){
    test2(*string)// Add * to pass the variable argument
}

fun test2(vararg string: String){}Copy the code

Infix call:

// Allow functions to be called using infix symbols. The function returns a Pair
infix fun Any.with(other:Any) = Pair(this,other)
Copy the code
val c = "3" with 4 // c is pair
val (f, s) = "3" with 4 // Val (f,s) is called a destruct declaration, giving the Pair's first to f and second to s.
println("f:$f,s:$s")

// Output the result
f:3,s:4
Copy the code

4. Program logic control

4.1 If with a return value

Instead of the ternary operator, Kotlin uses an if statement with a return value. The return value is the last line of code in each condition

//Kotlin uses the last line of code in each condition as the return value
fun largeNumber(number1: Int,number2: Int) : Int{
    return if(number1 > number2){
      	number1
    }else {
      	number2
    }
}

// Based on the syntax sugar and Kotlin type derivation mechanism we learned above, we could also abbreviate the largeNumber function, which ended up like this
fun largeNumber(number1: Int,number2: Int) = if(number1 > number2) number1 else number 2

Copy the code

4.2 When with a return value

Switch does not work very well in Java:

①Switch statements can only support certain types, such as integer, shorter than integer, string, enumerated type. If we are not using these types, Switch is not available

② All case conditions of Switch statements must have a break at the end

All of these issues have been addressed in Kotlin, and Kotlin has added a number of powerful new features:

The last line of code in the condition is the return value

④ The WHEN conditional statement allows any type of argument to be passed in

⑤ When condition body conditional format: matching value -> {execute logic}

⑥ Like the if statement, the {} in the body of the condition can be omitted if there is only one line of code in the body

//when has parameters
fun getScore(name: String) = when (name) {
    "tom" -> 99
    "jim" -> 80
    "lucy" -> 70
     else -> 0
}

// If there are no arguments in Kotin, use the == operator to determine whether strings or objects are equal
fun getScore(name: String) = when {
    name == "tom" -> 99
    name == "jim" -> 80
    name =="lucy" -> 70
    else -> 0
}

Copy the code

4.3 Loop Statements

There are two main types of loop statements in Kotlin :while and for-in. While is the same as in Java.

For-in in Kotlin is much more user-friendly than in Java.

for-in:
/ / to use.. Create an ascending range with closed ranges at both ends [0,10]
    for (i in 0.10.){
        print("$i ")}// Print the result
0 1 2 3 4 5 6 7 8 9 10

Copy the code
for-until:
// use until to create an ascending interval with the left end closed and the right end open [0,10]
    for (i in 0 until 10){
        print("$i ")}// Print the result
0 1 2 3 4 5 6 7 8 9
Copy the code
for-downTo:
// use downTo to create a descending interval where both ends are closed [10,0]
    for (i in 10 downTo 0){
        print("$i ")}// Print the result
10 9 8 7 6 5 4 3 2 1 0
Copy the code
Step by step:
// Use downTo to create descending ranges with closed ranges at both ends, skipping three elements at a time
    for (i in 10 downTo 0 step 3){
        print("$i ")}Copy the code
Iterative list
// Use withIndex to iterate through the list
 val list = arrayListOf("10"."11"."100")
 for ((index,element) in list.withIndex()){// Destruct the statement
        println("$index:$element")}// Print the result
0:10
1:11
2:100
Copy the code
Iterative map
 val map = mapOf(1 to "Java".2 to "Kotlin".3 to "Flutter")// infix call
 for ((key, value) in map) { / / deconstruction
     println("$key:$value")}// Print the result
1:Java
2:Kotlin
3:Flutter
Copy the code

Class 5.

As with Java, classes are defined as follows

 class Base {
     var num = 1
    }
Copy the code

But the meaning is not quite the same.

5.1 the visibility

** In Kotlin, the default class visibility is public and final. Inner classes are static by default, and non-static inner classes are marked with inner.

① Visibility in Kotlin
  • private: private, visible inside this class
  • protected: Subclass visible
  • internal: Visible in the module
  • public: Default, public
(2) compare the Java
  • private: private, visible inside this class
  • protected: Subclass visible
  • default: The default is visible within the package
  • public: public

Single constructor & private constructor

class Response private constructor(val code: Int.val msg: String){
         override fun toString(a): String {
            return "code:$code,msg:$msg"}}Copy the code

Multiple constructors

// Non-open cannot be inherited
class Response {
        val code: Int
        val msg: String

        constructor(code: Int) {
            this.code = code
            msg = ""
        }

        constructor(code: Int, msg: String) {
            this.code = code
            this.msg = msg
        }

        override fun toString(a): String {
            return "code:$code,msg:$msg"}}Copy the code

Getters for code and MSG are generated automatically.

In Kotlin, there is also a single inheritance and multiple implementations. When common, the inheritance class is written to the first place, followed by a comma to keep up with the interface interface.

public class String : Comparable<String>, CharSequence{. }Copy the code

Sealed class (private construct, default open)

sealed class Expr {

    class Num(val value: Int) : Expr()
    class Sum(val left: Expr, val right: Expr) : Expr()

}
Copy the code

5.2 the Object keyword

(1) the singleton class
object SingleTon {

    @JvmStatic
    fun isEmpty(str: String) = str.isEmpty()
}
Copy the code

Decompile to Java

public final class SingleTon {
   public static final SingleTon INSTANCE;

   @JvmStatic
   public static final boolean isEmpty(@NotNull String str){
      Intrinsics.checkParameterIsNotNull(str, "str");
      CharSequence var1 = (CharSequence)str;
      boolean var2 = false;
      return var1.length() == 0;
   }

   private SingleTon(a) {}static {
      SingleTon var0 = newSingleTon(); INSTANCE = var0; }}Copy the code

As you can see from the surface, Object modifies a single class and is represented as a singleton implemented by a static code block.

For @jvmstatic methods, Kotlin adds the static keyword to the method, and the static keyword does not exist in Kotlin.

(2) the anonymous class
test(object : Listener {

            })
interface Listener{}Copy the code

There is no new keyword in Kotlin, so anonymous classes are implemented with Object.

③ Associated objects

Kotlin does not have static, so how to implement the definition and use of static variables, constants, etc.

The answer is the companion

class UserManager {

    companion object{
        val USER_TYPE = 0x01}}Copy the code

The companion Object above generates an inner class Companion and adds a static getter that returns USER_TYPE, as follows

public final class UserManager {
   private static final int USER_TYPE = 1;
   public static final UserManager.Companion Companion = new UserManager.Companion((DefaultConstructorMarker)null);

   public static final class Companion {
      public final int getUSER_TYPE(a) {
         returnUserManager.USER_TYPE; }... }}Copy the code
PS: the const keyword

The const keyword can be used only in static classes, only with val, that is, const val, and only with primitive types. Meaning is a compile-time constant, replaced with the value of that constant where used. As follows:

object SingleTon {
    const val str = "const"
}


fun test(a): String? {
    return SingleTon.str
}
Copy the code

Test decompiles Java as follows:

 public final String test(a) {
     return "const";
}
Copy the code

You can see that Kotlin inline the const constant.

5.3 class delegate

equals

In Java, you can use == to compare primitive data types, which compare values, and reference types, which compare references. In Kotlin == is equal to calling Equals in Java. If you want to compare references, use ===.

By the keyword

Decorator pattern code is usually more template, and kotlin uses the by keyword to delegate classes. Such as:

class MyList<T> : List<T> by ArrayList() {
    // ArrayList implements all the interfaces of List by default
}
Copy the code

Convert to Java:

public final class MyList implements List.KMappedMarker {
   // $FF: synthetic field
   private final ArrayList $$delegate_0 = newArrayList(); .public Object get(int index) {
      return this.$$delegate_0.get(index); }... }Copy the code

Of course, we can also rewrite our own logic.

By can also be used to implement lazy loading:

private val arr by lazy { MyList<String>() }
Copy the code

It is implemented as double-check lazy loading, as follows:

private class SynchronizedLazyImpl<out T>(initializer: () -> T, lock: Any? = null) : Lazy<T>, Serializable {
    private var initializer: (() -> T)? = initializer
    @Volatile private var _value: Any? = UNINITIALIZED_VALUE
    // final field is required to enable safe publication of constructed instance
    private vallock = lock ? :this

    override val value: T
        get() {
            val _v1 = _value
            if(_v1 ! == UNINITIALIZED_VALUE) {@Suppress("UNCHECKED_CAST")
                return _v1 as T
            }

            return synchronized(lock) {
                val _v2 = _value
                if(_v2 ! == UNINITIALIZED_VALUE) {@Suppress("UNCHECKED_CAST") (_v2 as T)
                } else {
                    valtypedValue = initializer!! () _value = typedValue initializer =null
                    typedValue
                }
            }
        }

    override fun isInitialized(a): Boolean= _value ! == UNINITIALIZED_VALUEoverride fun toString(a): String = if (isInitialized()) value.toString() else "Lazy value not initialized yet."

    private fun writeReplace(a): Any = InitializedLazyImpl(value)
}
Copy the code

6.lambda

6.1 grammar

Lambda expressions are essentially small pieces of code that can be passed to other functions.

Simple lambda in Kotlin:

button.setOnclickListener{
    
}
Copy the code

Lambda in Kotlin is always surrounded by curly braces {}. Lambda expressions can be stored in variables:

val sum = { x:Int,y:Int -> x + y }
println(sum(1.2))
 
3
Copy the code

In Kotlin, lambda as the last argument can be placed after () as follows: 1; If lambda is the only argument, you can omit (), as follows 2.

list.maxBy({it.length})
list.maxBy(){it.length}/ / 1
list.maxBy{it.length}/ / 2
Copy the code

6.2 Set function API

For example, list filtering:

val list = arrayListOf(1.2.3.4.5.6.7.8.9.10)
list.filter { it % 2= =0 }
    .forEach { print(it) }

// Print the result
246810
Copy the code

Similarly:

All: Returns true for all matches

Any: Returns true if a match exists

Count: Returns the number of matches

FirstOrNull: The first match, no NULL is returned

First: The first match, no exception thrown

Find: Finds the first match equal to firstOrNull

There is a map, flatmap, groupby…

It basically covers common RxJava operators.

apply,let,also…

Apply changes this to refer to the caller. Facilitates various operations and returns the caller

 val str = "123456"
 val rec = str.apply {
     println("lastChar:${get(lastIndex)}")
  }
  println("rec:$rec")


// Print the result
lastChar:6
rec:123456
Copy the code

With changes this to the argument, returning the result of the last line of the lambda

Let creates a local variable that returns the last line of results.

   val str :String ? = "123456"
   valres = str? .let { println("it:$it")
                "return"
    }
    println("res:$res")

// Print the result
it:123456
res:return
Copy the code

Also: Creates it and returns the caller

Run: Changes this to point to the caller and returns the last line

Similar syntactic sugar takeIf, repeat, etc., are defined in standard.kt.

Second, in-depth understanding

1. Kotlin type system

1.1 can be empty

Kotlin raises ERROR: Type Mismatch at run time when a null is received for a Type definition that has not been added nullable.

When a class is specified to be nullable, can a security call be used? Can you follow the Elvis operator? :. Sign in front? . Executed when the caller is null.

val str :String ? = "123456"str? .get(str.lastIndex) ? : toast("str is null") //toast is executed when STR is null
Copy the code

Safe conversion :as?

Is: checks the type and can be automatically transformed

 val obj: Any = "str"
if (obj is String) {
   println("obj:$obj")//obg automatically converts to string
}

// Print the result
obj:str
Copy the code

As: Type conversion, as? Safe type conversion

val obj: Any = "str"
(obj as? String)? .print()If obj is String, print is a locally defined extension function

// Print the result
str
Copy the code

!!!!! Not empty assertion

Have Kotlin throw an exception when the object is empty.

   val obj: Any? = "str"obj!! .toString() obj.hashCode()// No need to add more!! , the Kotlin compiler automatically checks
Copy the code

2. Operator overload

data class Point(val x: Int.val y: Int) {
    operator fun plus(other: Point): Point {
        return Point(x + other.x, y + other.y)
    }
}

  val p1 = Point(1.2)
  val p2 = Point(5.6)
  val p = p1 + p2
  println("p:$p")// Automatically call toString

// Print the result
p:Point(x=6, y=8)
Copy the code

Operators available for overloading:

expression The function name
a * b times
a / b div
a % b mod
a + b plus
a – b minus

PS: Bit operations also have their own notation

The operator operation
shl Shift the sign to the left
shr Move to the right with the sign
ushr Unsigned shift to the right
and Bitwise and
or Bitwise or
xor The bitwise exclusive or
inv According to the not

3. Lambda as a parameter

No other parameters:

 fun exec(func:()->Unit){
     func.invoke()
 }
 exec { 
   println("hello world")}Copy the code

With other parameters:

 fun exec(msg: String,func:(msg:String) - >Unit){
      func.invoke(msg)
 }
 exec("hello world") {msg->
    println(msg)
 }
Copy the code

Lambda above is taken as a parameter that cannot be empty. If it is empty, it needs to be defined as ()? The parcel. As follows:

 fun exec(msg: String,func:((msg:String) - >Unit)?{ func? .invoke(msg) }Copy the code

Lambda is good to pass as an argument, but its implementation still passes objects (anonymous classes). An object is created on every call. How to avoid this overhead and improve performance?

The answer is an inline function.

4. Series of inline functions

inline

When a function is declared inline, its function body is inline — in other words, the function is replaced directly where the function was called, rather than being called normally.

inline fun inlineTest(inlineFunc:()->Unit){
    println("before invoke")
    inlineFunc()
    println("after invoke")
}

  inlineTest {
        println("hello world")}// Print the result
before invoke
hello world
after invoke
Copy the code

to Java:

String var2 = "before invoke";
System.out.println(var2);
String var5 = "hello world";
System.out.println(var5);
var2 = "after invoke";
System.out.println(var2);
Copy the code
Limitations on inline functions

When we add a return to the inline lambda, the code doesn’t work correctly; it returns halfway through.

  inlineTest {
        println("hello world")
        return
    }
 println("code works as well")

// Print the result
before invoke
hello world
Copy the code

In this case, we need to add the scope for return

  inlineTest {
        println("hello world")
        return@inlineTest
    }
   println("code works as well")

// Print the result
before invoke
hello world
after invoke
code works as well

Copy the code

You can also use other methods, such as noinline

noinline

Noinline modifies lambda arguments in an inline method. Noinline is used in cases where we do not want the inline feature to apply to some lambda arguments of an inline method.

    // Kotlin
fun main(args: Array<String>) {
        val methodName = "main"
        multiplyByTwo(5) {
            result: Int -> println("call method $methodName, Result is: $result")}}inline fun multiplyByTwo(
            num: Int.noinline lambda: (result: Int) - >Unit): Int {
        val result = num * 2
        lambda.invoke(result)
        return result
    }
Copy the code

The result of the decompilation is:

 public final void main(@NotNull String[] args) {
      Intrinsics.checkParameterIsNotNull(args, "args");
      final String methodName = "main";
      byte num$iv = 5;
      Function1 lambda$iv = (Function1)(new Function1() {
         // $FF: synthetic method
         // $FF: bridge method
         public Object invoke(Object var1) {
            this.invoke(((Number)var1).intValue());
            return Unit.INSTANCE;
         }

         public final void invoke(int result) {
            String var2 = "call method " + methodName + ", Result is: " + result;
            boolean var3 = false; System.out.println(var2); }});int $i$f$multiplyByTwo = false;
      int result$iv = num$iv * 2;
      lambda$iv.invoke(result$iv);
   }

   public final int multiplyByTwo(int num, @NotNull Function1 lambda) {
      int $i$f$multiplyByTwo = 0;
      Intrinsics.checkParameterIsNotNull(lambda, "lambda");
      int result = num * 2;
      lambda.invoke(result);
      return result;
   }
Copy the code

As you can see, because the lambda is decorated with noinline, the compiler processes the lambda as an anonymous inner class, generating a Function1 object.

crossinline

Is inline and noinline enough for us developers? Is that enough? Obviously not. Consider a case where we want the lambda to be inline as well, but we don’t want the lambda to affect the caller’s control flow. This can be due to conscious active control, but most of the time it is due to developer carelessness. We know that the Java language is a compiled language, so it would be safe to indicate or even report errors during compilation where inline lambda affects the caller’s control flow.

Crossinline was created to deal with this situation. Crossinline retains the inline feature, but an error is reported if a return is attempted within the incoming lambda. Return can only return the current lambda.

    // Kotlin
    fun main(args: Array<String>) {
        val methodName = "main"
        multiplyByTwo(5) {
            result: Int -> println("call method $methodName, Result is: $result")
            return@multiplyByTwo}}Copy the code

As shown in the code, you must return@multiplyByTwo instead of writing return directly.

5. The generic

5.1 Basic usage of generics

1) First, let’s explain what generics are. Generics are parameterized types that allow us to program without specifying specific types. When we define a class, method, or interface, and we add a type parameter to it, we add a generic type to the class, method, or interface

// define a generic class. Use 
      
        after the class name to define a generic class
      
class MyClass<T>{
  	fun method(params: T){}}// Generic calls
val myClass = MyClass<Int>()
myClass.method(12)

// define a generic method by adding 
      
        to the name of the method
      
class MyClass{
    fun <T> method(params: T){}}// Generic calls
val myClass = MyClass()
myClass.method<Int> (12)
// According to the Kotlin type inference mechanism, we can omit generics
myClass.method(12)

// define a generic interface by adding 
      
        to the name of the interface
      
interface MyInterface<T>{
    fun interfaceMethod(params: T)
}

Copy the code







type





class MyClass{
  	// We specify that the upper bound of the generic type is Number, so we can only pass in numeric parameters
  	fun <T : Number> method(params: T){}}Copy the code
Generics firm

Note that generics on the JVM are generally implemented by generic erasure, so the same is true for generics on Kotlin. But Kotlin can reified a generic with an inline function

inline fun <reified T>可迭代< * >.filterIsInstance(a): List<T> { //reified is no longer erased
    val des = mutableListOf<T>()
    forEach {
        if (it is T) des.add(it)
    }
    return des
}

fun main(args: Array<String>) {
    val items = arrayListOf("one".2."three")
    println(items.filterIsInstance<String>())
}

// Print the result
[one, three]
Copy the code

Why can Kotlin avoid generics? Because the Kotlin compiler inserts the bytecode that implements the inline function into each call, it takes the actual type as an argument.

5.2 Covariant and contravariant

In Java, storing a reference to a Source instance in a variable of type Source is extremely safe — there are no consumer-methods to call. But Java doesn’t know this and still forbids it:

void demo(Source<String> strs) {
  Source<Object> objects = strs; / /!!!!!! Not allowed in Java
  / /...
}
Copy the code

To fix this, we must declare the object of type Source<? Extends Object>, which makes no sense because we can call all the same methods on this Object as before, so the more complex types don’t add value. But the compiler doesn’t know that.

So in Kotlin, there’s a way to explain this to the compiler. This is called declarative typing: we can annotate the Source parameter type T to ensure that it is returned only from the Source member (read only, equivalent to Java? Extends T). To do this, Kotlin provides the out modifier.

//kotlin
interface Source<out T> {
    fun nextT(a): T
}
fun demo(strs: Source<String>) {
    val objects: Source<Any> = strs // This is ok because T is an out- argument
    / /...
}
Copy the code

In simple terms: consumers use in, producers use out

As a general rule, when the type parameter T of a class C is declared out, it can only appear in the output of C members, but the payoff is that C is safe as a superclass of C. In addition to out, Kotlin added a variant note: in. It inverts a type parameter: it can only be written to, but not read. Super T). A good example of an inverter type is Comparable:

interface Comparable<in T> {
    operator fun compareTo(other: T): Int
}

fun demo(x: Comparable<Number>) {
    x.compareTo(1.0) // 1.0 has the type Double, which is a subtype of Number
    // Therefore, we can assign x to variables of type Comparable 
      
    val y: Comparable<Double> = x / / OK!
}
Copy the code

6.DSL

6.1 the DSL is introduced

The full name of DSL is Domain specific language, such as HTML and XML

The characteristics of

  • Solve domain-specific problems
  • It and system programming language go is two extremes, system programming language is to solve all the problems, for example, Java language hopes to do Android development, but also hope to do background development, it has the characteristics of horizontal expansion. DSLS have the ability to solve domain-specific problems in vertical depth.

In general, the core idea of DSLS is to solve domain-specific problems by focusing on specialization rather than completeness.

6.2 Kotin DSL

Gradle is an open source build automation tool. Gradle is a Groovy or Kotin-based DSL. Our Android application is built using Gradle, so we can use Kotlin to write scripts and plug-ins. Besides, AndroidStudio is very friendly to Kotlin, and it is very cool to write.

Such as:

Example 1- Requesting a callback:

open class RequestCallback<T> : Callback<T> {

    private val builder: Builder<T>
    
	...

    private fun onSuccess(data: T){ builder.onSuccess? .invoke(data)}private fun onError(code: Int, msg: String?).{ builder.onError? .invoke(code, msg) ? : toast(msg) }private fun onFinished(a){ decrement() builder.onFinished? .invoke() }class Builder<T> {
        internal var onSuccess: ((data: T) -> Unit)? = null
        internal var onError: ((code: Int, msg: String?) -> Unit)? = null
        internal var onFinished: (() -> Unit)? = null

        fun onSuccess(func: ((data: T) - >Unit)? {
            onSuccess = func
        }

        fun onError(func: ((code: Int.msg: String?). ->Unit)? {
            onError = func
        }

        fun onFinished(func: (() -> Unit)? {
            onFinished = func
        }
    }
}

   fun <T> getCallback(refresh:Boolean = false,dsl: RequestCallback.Builder<T>. () - >Unit): RequestCallback<T> {
     ...
        return RequestCallback(b)
    }

/ / useapi.fetchQrCode(orderNo, faceSuccess).enqueue(getCallback { onSuccess { ... } onError { _, msg -> ... }})Copy the code

Example 2- Extend system Animator listener:

class AnimationCallbackBuilder {
    internal var onRepeat: ((animation: Animator?) -> Unit)? = null
    internal var onCancel: ((animation: Animator?) -> Unit)? = null
    internal var onStart: ((animation: Animator?) -> Unit)? = null
    internal var onEnd: ((animation: Animator?) -> Unit)? = null

    fun onRepeat(function: ((animation: Animator?). ->Unit)? {
        this.onRepeat = function
    }

    fun onCancel(function: ((animation: Animator?). ->Unit)? {
        this.onCancel = function
    }

    fun onStart(function: ((animation: Animator?). ->Unit)? {
        this.onStart = function
    }

    fun onEnd(function: ((animation: Animator?). ->Unit)? {
        this.onEnd = function
    }
}

fun AnimatorSet.registerCallback(listener: AnimationCallbackBuilder. () - >Unit) {
    val mListener = AnimationCallbackBuilder().also(listener)
    addListener(object : Animator.AnimatorListener {
        override fun onAnimationRepeat(animation: Animator?).{ mListener.onRepeat? .invoke(animation) }override fun onAnimationEnd(animation: Animator?).{ mListener.onEnd? .invoke(animation) }override fun onAnimationCancel(animation: Animator?).{ mListener.onCancel? .invoke(animation) }override fun onAnimationStart(animation: Animator?).{ mListener.onStart? .invoke(animation) } }) }/ / use

    set.registerCallback {
        onEnd {
          
        }
    }


Copy the code

In Kotlin, specifying that a method returns a nullable type requires a? It’s tricky to tell if it’s empty. Such as:

// You can also define your own apply
inline fun <T> T.exist(block: T. () - >Unit) = block()
inline fun curActivity(block: Activity. () - >Unit)= ActivityCollector.currentActivity()? .let(block)/ / useitem? .exist{this.toString()// This changes the direction of this, and this is not null
}

curActivity{
    startActivity(intent)/ / same as above, this is ActivityCollector currentActivity () to obtain the activity is not null,
    // If ** is empty, do not execute **
}
Copy the code

other

Interface with default implementation, such as screenshot permission configuration:

interface ICaptureView {

    fun canCapture(a) = false
}
Copy the code

Classes that can’t be screenshots only need to implement interfaces that can be overwritten.

Ktorm

What is Ktorm?

Ktorm is an efficient and concise lightweight Kotlin ORM framework written directly based on pure JDBC. It provides strongly typed and flexible SQL DSLS and convenient sequence apis to reduce the repetitive effort of operating databases. Of course, all SQL is automatically generated. Ktorm is open source based on Apache 2.0 protocol, source code hosted on GitHub, if it is helpful to you, please leave your star:kotlin-orm/ktorm

features

  • No configuration files, no XML, no annotations, or even any third party dependencies, lightweight, simple and easy to use
  • Strongly typed SQL DSLS expose low-level bugs at compile time
  • Flexible queries with arbitrary precision control over the SQL generated
  • Entity sequence API, usedfilter,map,sortedByIs as convenient as using native collections in Kotlin
  • Easy to expand design, you can write extensions flexibly, support more operators, data types, SQL functions, database dialects, etc

conclusion

Kotlin has several advantages over Java:

①Kotlin’s syntax is more concise, and the amount of code developed using Kotlin can be reduced by 50% or more than Java development

(2) Kotlin’s syntax is more advanced. Compared to Java’s old syntax, Kotlin has added many of the syntax features of modern high-level languages (syntax sugar), greatly improving our development efficiency

Kotlin is 100% compatible with Java. Kotlin can call code written in Java directly and seamlessly use Java third-party open source libraries, which allows Kotlin to add many new features while inheriting all the wealth of Java

.

There are some disadvantages as well:

①Kotlin generates more classes than Java after compilation, which may affect the size of the installation package.

(2) Although Kotlin is fully compatible with Java, some functions are not very convenient and beautiful to use in Java, so if the library author has strict requirements for code (beautiful), it is best to wrap a Java call layer in the outer layer.

HttpManager.INSTANCE.create(MineApi.class).getCashRecordList(map).enqueue(new RequestCallback<>(baseResponseBuilder -> {/ / DSL becomes as follows, then a lambda Function, once Function1, Function2 (digits) according to the parameters such as
            baseResponseBuilder.onSuccess(new Function1<BaseResponse<ArrayList<CashRecordListBean>>, Unit>({
                @Override
                public Unit invoke(BaseResponse<ArrayList<CashRecordListBean>> arrayListBaseResponse). returnnull; }});return null;
        }));
Copy the code

③Kotlin’s KAPT needs to generate stub(Java code) that APT can parse, and then use APT to generate code, which affects the performance of KAPT and slows down the overall compilation speed of Kotlin project.

Kotlin also released an alpha version of the native Processing KSP, which claims to bring twice the speed [Announcing Kotlin Symbol Processing (KSP) alpha]

KSP offers a powerful and yet simple API for parsing Kotlin code directly, Dramatically reducing the build speed tax by KAPT’s stub generation. Indeed, initial benchmarks with the Room library show that KSP is approximately 2x faster than KAPT.

Overall, Kotlin brings more advantages than disadvantages, a concise and easy-to-use syntax, and much less code. A practical type system that avoids most null pointer exceptions. It’s also fully compatible with Java, and with Google announcing Kotlin First, Android development will migrate to Kotlin sooner or later. Plus, with the release of KSP, Kotlin’s slow compilation issues will be resolved, and the ecosystem will get better and better.

The resources

Kotlin Combat [Russia]Dmitry Jemerov, Svetlana Isakova

Kotlin inline, noinline and crossinline_

“Kotlin” series: 1. Introduction to Kotlin

Learn by comparing the Kotlin keywords out and in with generics in Java

[Announcing Kotlin Symbol Processing (KSP) Alpha]

OneMoreThing

Kotlin released the Coroutines library, which claims to be thread-based Coroutines. Next issue preview:

Has Kotlin really implemented a JVM-based coroutine?

What does suspend in Kotlin coroutine mean?

How to optimize multiple Callback nesting?

How does Retrofit implement compatible coroutines internally?

How to use coroutines to complete page lifecycle management (requests, background tasks)?