Code Xiaosheng, a technology platform focused on the Android field

Join my Android technology group

Author: small village doctor

Link: https://www.jianshu.com/p/f7deb4fe6427

Disclaimer: This article has been published with the authorization of the village doctor. Please contact the original author for forwarding

Associated object

The static keyword does not exist in Kotlin. This is where Kotlin’s companion objects are used.

Object declarations within a class can be marked with the Companion keyword:

class MyClass {    companion object Factory {        fun create(): MyClass = MyClass()    }}Copy the code

Members of the companion object can be called by using only the class name as a qualifier:

val instance = MyClass.create()Copy the code

You can omit the name of the Companion object, in which case the name Companion will be used:

class MyClass {    companion object { }}val x = MyClass.CompanionCopy the code

The role of the companion object

Similar to the syntax in Java for using classes to access static members. Because Kotlin eliminated the static keyword, Kotlin introduced companion objects to compensate for the lack of static members. As you can see, the primary role of the companion object is to simulate static members for the external class in which it resides.

Call the companion object in your Java code

How do I call Kotlin’s companion objects in Java code?

public static void main(String[] args) { System.out.println(MyClass.Factory.create()); System.out.println(MyClass.Companion.create()); }Copy the code
  • If the associated object has a name, use:

Class name. Associated object name. Method name () Class name. Half-life object name. Property setter,getter methodsCopy the code
  • If the Companion object is declared to have no name, the Companion keyword is called:

Class name. Companion. Method name () Class name. Companion. Property setter,getter methodCopy the code

Use of @jVMField and @jVMStatic

In the example above, we learned that you can call a member of a companion object in Kotlin in Java code, similar to a static member in a Java class. But it looks slightly different from Java, with the Companion object name or Companion keyword between the class name and method name/property setter and getter method name. How do you make it look the same when called in Java?

Kotlin provides us with @jVMField and @jVMStatic annotations. @jVMField is used for attributes and @jVMStatic is used for methods. Such as:

class Test {    companion object {        @JvmField        val flag = true        @JvmStatic        fun add(a: Int, b: Int): Int {            return a + b        }    }}Copy the code

This way we call a static member in the same way that a Java class calls a static member in the same way that the Kotlin code calls:

System.out.println(Test.flag); System.out.println(Test.add(1, 2));Copy the code

The const keyword

In the companion object, we may need to declare a constant that is intended to be equivalent to static constants in Java. There are two ways to do this, one using the @jVMField annotation as mentioned above, and the other using the const keyword. Both declarations are equivalent to Java static Final variables. The following code:

companion object {    const val flag = true    @JvmStatic    fun add(a: Int, b: Int): Int {        return a + b    }}Copy the code

Extend properties and extend methods

Extension function

Kotlin extension functions are functions that you can call as a class member, but are defined outside the class. This makes it easy to extend an existing class and add additional methods to it

Now let’s add a toInt method to String

package com.binzi.kotlinfun String? .toInt(): Int { return java.lang.Integer.parseInt(this)}Copy the code

In this extension function, you can directly access the functions and attributes of your extended class, just like the methods defined in this class, but extension functions do not allow you to break the wrapper. Unlike methods defined in a class, it does not have access to private, protected methods and properties.

Extension function import

We define extension functions directly in the package. This way we can use the extension throughout the package, and if we want to use another package’s extension, we need to import it. Importing extension functions is the same as importing classes.

Import com.binzi.kotlin.toInt or import com.binzi.kotlin.*Copy the code

In some cases, you may be importing third-party packages that extend the same function name to the same type. To resolve the conflict, you can rename the extension function in the following way

import com.binzi.kotlin.toInt as toIntegerCopy the code

Extension functions cannot be overridden

Principles of extension methods

Kotlin extension methods of a class is not in the original class, expand inside through the compilation for Java code, can be found that the principle is the use of decorative patterns, and packaging to the operation of the source class instance, as real as we defined in the Java class methods, and the tool class method is to use the caller as the first parameter, The caller is then manipulated in the tool method

Such as:

fun String? .toInt(): Int { return java.lang.Integer.parseInt(this)}Copy the code

Decompile to the corresponding Java code:

public final class ExtsKt {   public static final int toInt(@Nullable String $this$toInt) {      return Integer.parseInt($this$toInt);   }}Copy the code

Extended attributes

The principle of extension properties of a class is the same as that of extension methods, but the form of definition is different. Extension properties must define get and set methods

Extend a firstElement property for MutableList:

Var <T> MutableList<T>.firstElement: T get() {return this[0]} set(value) {this[0] = value}Copy the code

The decompiled Java code looks like this:

   public static final Object getFirstElement(@NotNull List $this$firstElement) {      Intrinsics.checkParameterIsNotNull($this$firstElement, "$this$firstElement");      return $this$firstElement.get(0);   }   public static final void setFirstElement(@NotNull List $this$firstElement, Object value){      Intrinsics.checkParameterIsNotNull($this$firstElement, "$this$firstElement");      $this$firstElement.set(0, value);   }Copy the code

The inner class

Kotlin’s inner classes are a little different from Java’s inner classes, which can access the members of the outer class directly. Kotlin’s inner classes cannot access the members of the outer class directly. They must be marked with inner to access the members of the outer class

  • There is no inner class that uses the inner tag

Class A{var A = 0 class B{// var B = 1}}Copy the code

Decompiled Java code

public final class A { private int a; public final int getA() { return this.a; } public final void setA(int var1) { this.a = var1; } public static final class B { private int b = 1; public final int getB() { return this.b; } public final void setB(int var1) { this.b = var1; }}}Copy the code
  • Inner class marked with inner

Class A{var A = 0 inner class B{var B = A}}Copy the code

Decompiled Java code

public final class A { private int a; public final int getA() { return this.a; } public final void setA(int var1) { this.a = var1; } public final class B { private int b = A.this.getA(); public final int getB() { return this.b; } public final void setB(int var1) { this.b = var1; }}}Copy the code

As you can see from the above, inner classes that do not use the inner tag end up generating static inner classes, while inner classes that use the inner tag generate non-static inner classes

Anonymous inner class

Anonymous inner classes are primarily for those that get abstract classes or interface objects. The most common anonymous inner class View click events:

Btn.setonclicklistener (new OnClickListener() {@override public void onClick(View v) {}});Copy the code

Kotlin does not have the new keyword, so how to write kotlin anonymous inner class?

btn.setOnClickListener(object : View.OnClickListener{    override fun onClick(v: View?) {        print("1111")    }})Copy the code

The argument to this method is an anonymous inner class. Write object: and then write your argument type view.onClickListener {}.

Kotlin has another handy lambda expression:

btn.setOnClickListener { print("1111") }Copy the code

Data classes

There are no specialized data classes in Java, usually through Javabeans, but there are specialized data classes provided in Kotlin.

  • Java

public class Person {    private int age;    private String name;    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }}Copy the code

As you can see from the above example, if you want to use a data class, you need to write the corresponding setter/getter methods manually (although the IDE can also generate them in batches), but from a code reading perspective, a lot of seeter/getter methods are not very readable and maintainable in the case of lots of attributes.

  • Kotlin In Kotlin, data classes can be generated using the keyword data:

data class Person(val name: String, val age: Int)Copy the code

Add the data keyword before the class keyword. The compiler generates the corresponding data classes based on the parameters in the main constructor. Automatic generation of setter/getter, toString, hashCode, and other methods

To declare a data class, you need to:

  • The main constructor has at least one argument

  • All arguments in the main constructor need to be marked as val or var

  • Data classes cannot be abstract, developed, sealed, and internal

Enumeration class

An enumerated class is a special class that Kotlin can define with the enum class keyword.

Enumeration classes can implement 0~N interfaces;

  • Enum classes inherit from kotlin.Enum by default (all other classes end up with Any as their parent), so kotlin enumerations cannot inherit from classes;

  • A non-abstract enumeration class cannot be modified with the open modifier, so a non-abstract enumeration class cannot subclass;

  • Abstract enumeration classes cannot use the abstract keyword to modify enum classes. Abstract methods and properties are required.

  • Enumeration class constructors can only be decorated with the private modifier and default to private if not specified.

  • All instances of the enumerated class are listed explicitly on the first line, separated by commas, and the entire declaration ends with a semicolon;

  • Enumerated classes are special classes that can also define properties, methods, and constructors;

  • Enumeration classes should be made immutable, that is, property values are not allowed to change, which is safer;

  • When an enumeration property is set to read-only, it is best to specify an initial value for the enumeration class in the constructor. If you specify an initial value for an enumeration at declaration time, this will result in the property being the same for all enumeration values (or enumerated objects).

Defining enumeration classes

/** * Define an enumeration class */enum class EnumClass(val enumParam: String) {MON(" Monday "), TUES(" Tuesday "), WED(" Wednesday "); /** * Enumeration class method */ fun enumeration fun () {println(" enumeration value: $this enumeration property: $enumParam")}}Copy the code

Enumeration classes implement interfaces

  1. Enumerated values implement abstract members of the interface, respectively

enum class EnumClass(val enumParam: String) : EnumInterface {MON(" MON ") {override fun interfaceFun() {println(enumParam)} override val interfaceParam: String get() = "1"}, TUES(" Tuesday ") {override fun interfaceFun() {println(enumParam)} override val interfaceParam: String get() = "2"}, WED(" WED ") {override fun interfaceFun() {println(enumParam)} override val interfaceParam: String get() = "3" }; }interface EnumInterface {fun interfaceFun() val interfaceParam: String}Copy the code
  1. Enumeration classes uniformly implement the abstract members of an interface

Enum class EnumClass(val enumParam: String) : EnumInterface {MON(" Monday "), TUES(" Tuesday "), WED(" Wednesday "); Override fun interfaceFun() {} Override val interfaceParam: String get() = {}}Copy the code
  1. Implement abstract enumeration class abstract member respectively

enum class AbstractEnumClass { MON { override val abstractParam: AbstractFun () {println(abstractParam)}}, TUES {override val abstractParam: String get() = override fun abstractFun() {println(abstractParam)}}, WED {override val abstractParam: String get() = "override fun abstractFun() {println(abstractParam)}}; abstract val abstractParam: String abstract fun abstractFun()}Copy the code

entrust

Delegation pattern is a basic skill in software design pattern. In the delegate pattern, two objects participate in processing the same request, and the receiving object delegates the request to the other object. The delegate pattern is a basic skill, and many other patterns, such as the state pattern, the policy pattern, and the visitor pattern, essentially use the delegate pattern for more specific situations. The delegate pattern allows us to substitute aggregation for inheritance.

Delegate in Java:

interface Printer { void print(String s)}class RealPrinter implements Printer { @override public void print(String s) { System.out.println(s); }}class PrintImpl implements Printer{// RealPrinter rp = new RealPrinter(); @override public void print(String s) { rp.print(s); }}class Demo { public static void main(String[] args) { Printer p = new PrintImpl(); p.print("hello world"); }}Copy the code

Kotlin:

interface Printer {  fun print(s: String)}class RealPrinter : Printer {  override fun print(s: String) {    println(s)  }}class PrintImpl(p: Printer) : Printer by pfun main(args: Array<String>) {  val ps = PrintImpl(RealPrinter())  ps.print("hello world")}Copy the code

By means that P will be stored internally in PrintImpl, and the compiler will automatically generate methods for all printers forwarded to P.

Entrusted property

There are some common attribute types that we can implement manually every time we need them, but it would be nice to implement them once for everyone and put them into a library. Examples include:

  • Lazy properties: its value is only evaluated on first access;

  • Observable Properties: Listeners receive notifications about changes to this property.

  • Store multiple attributes in a map instead of each in a separate field.

To cover these (and other) cases, Kotlin supports delegate attributes.

The syntax for delegate attributes is:

Var < attribute name >: < type > by < expression >Copy the code

The expression after BY is the delegate, because the property’s corresponding get() (and set()) are delegated to its getValue() and setValue() methods.

Standard commission:

The Kotlin library provides factory methods for several useful delegates.

  1. The delay attribute Lazylazy() takes a lambda and returns a Lazy

    The first call to get() executes the lambda expression passed to lazy() and records the result, and subsequent calls to get() simply return the result of the record. Such as:

val lazyValue: String by lazy { println("computed!" ) "hello"}fun main(args: Array<String>) {println(lazyValue) println(lazyValue)}// Output: // computed! // hello// helloCopy the code
  1. Observable properties ObservableDelegates. Observables () takes two parameters: the initial value and modifying the handler (handler). This handler is called whenever we assign a value to the property (executed after the assignment). It takes three arguments: the assigned property, the old value, and the new value:

class User { var name: String by Delegates.observable("<no name>") { prop, old, new -> println("$old -> $new") }}fun main(args: Array<String>) {val user = user () user.name = "first" user.name = "second"}// <no name> -> first// first -> secondCopy the code

Instead of observable(), you can use vetoable() instead of Observable (), which takes the same parameters as observable, but returns a Boolean that determines whether to adopt the new value. That is, the handler passed to vetoable is called before the attribute is assigned a new value to take effect. Such as:

class User { var num: Int by Delegates.vetoable(0) { property, oldValue, newValue -> newValue > oldValue }}fun main(args: Array<String>) {val user = user () user.num = 10 println(user.num) // 10>0, 10 user.num = 5 println(user.num) // 5<10Copy the code
  1. A common use case for storing attributes in a map is to store attribute values in a map. This is often seen in applications like parsing JSON or doing other “dynamic” things. In this case, you can implement delegate properties using the mapping instance itself as the delegate.

Such as:

class User(map: Map<String, Any? >) { val name: String by map val age: Int by map}fun main(args: Array<String>) { val user = User(mapOf("name" to "whx", "age" to 18)) println(user.name) // whx println(user.age) // 18}Copy the code

In the above example, the delegate attribute is evaluated from the map passed in by the constructor (by string key — the name of the attribute), and an exception is thrown if the key name for the declared attribute is not found in the map, or if the type of the value for the key is not the same as the type of the declared attribute.

Inline function

When a function is declared inline, its body is inline, that is, the function is replaced directly to where the function was called

An inline function is conceptually an inline function that the compiler replaces each function call with the actual code implemented by the function. The immediate benefit of the inline function is that it saves the overhead of the function call, while the disadvantage is that it increases the size of the generated bytecode. Based on this, is it necessary to define all functions inline when the code volume is not very large? Let’s illustrate it in two cases:

  1. Define ordinary functions as inline: It is well known that the JVM has achieved inline internal optimization, it will in any place can improve performance by inline will function call inline, and compared with the ordinary function is defined as an inline manually, inline optimization generated by the JVM bytecode, the realization of each function only once, so in guarantee to reduce the runtime overhead at the same time, Nor does it increase the size of the bytecode; Therefore, we can conclude that for ordinary functions, we do not need to declare them as inline functions, but leave them to the JVM to optimize.

  2. Defining functions with lambda arguments as inline: yes, it does improve performance in this case; However, in the process of using it, we will find that it has many limitations. Let’s start with the following example:

inline fun doSomething(action: () -> Unit) { println("Before doSomething..." ) action() println("After doSomething..." )}Copy the code

Suppose we call doSomething like this:

fun main(args: Array<String>) {    doSomething {        pringln("Hello World")    }}Copy the code

The above call will compile to:

fun main(args: Array<String>) { println("Before doSomething..." ) println("Hello World") println("After doSomething..." )}Copy the code

As you can see from the above compilation, both the doSomething function and the action argument are inlined, which is nice, so let’s call it a different way:

fun main(args: Array<String>) {    val action:() -> Unit = { println("Hello World") }    doSomething(action)}Copy the code

The above call will compile to:

fun main(args: Array<String>) { println("Before doSomething..." ) action() println("After doSomething..." )}Copy the code

The doSomething function is inlined, but the action argument is not. This is because the lambda passed to doSomething as a functional variable is not available at the point of call of the function. The lambda is not available until doSomething is inlined.

Using the above example, let’s do a quick summary of when lambda expressions are inlined:

  1. When a lambda expression is passed directly to an inline function as an argument, the code for the lambda expression is replaced directly with the resulting code.

  2. When a lambda expression is stored somewhere and then passed as a variable to an inline function, the code for the lambda expression will not be inline.

The inline timing of lambda was discussed above, but after a moment’s digestion let’s look at one final example:

inline fun doSomething(action: () -> Unit, secretAction: () -> Unit) {   action()   doSomethingSecret(secretAction)}fun doSomethingSecret(secretAction: () -> Unit) {}Copy the code

Is there anything wrong with the above example? Yes, the compiler throws “Illegal usage of inline-parameter” because Kotlin says lambda arguments in an inline function can only be called directly or passed to another inline function. So what if we do want to pass a lambda to a non-inline function? We can simply transform the above code like this:

inline fun doSomething(action: () -> Unit, noinline secretAction: () -> Unit) {   action()   doSomethingSecret(secretAction)}fun doSomethingSecret(secretAction: () -> Unit) {}Copy the code

It is easy to prefix lambda arguments that do not need to be inline with the noinline modifier.

That’s all I have to say about inline functions. By understanding how this feature works, I believe you can use it at the right time, rather than abuse it or abandon it out of fear.

Kotlin singleton pattern

Hangry implementation

Public class SingletonDemo {private static SingletonDemo instance=new SingletonDemo(); private SingletonDemo(){ } public static SingletonDemo getInstance(){ return instance; }//Kotlin implements object SingletonDemoCopy the code

LanHanShi

Public class SingletonDemo {private static SingletonDemo instance; private SingletonDemo(){} public static SingletonDemo getInstance(){ if(instance==null){ instance=new SingletonDemo(); } return instance; Private constructor() {companion object {private var instance: SingletonDemo? = null get() { if (field == null) { field = SingletonDemo() } return field } fun get(): SingletonDemo{// Return instance = SingletonDemo; return instance = SingletonDemo; }}}Copy the code

In the code above, we can see that in the Kotlin implementation, we privatized its main constructor and customized its property accessors, and the rest is pretty much the same.

  • If you’re not sure how the Kotlin constructor works. Please click — constructor

  • Not clear about Kotlin’s properties and accessors, please click properties and Fields

Thread-safe slob style

Public class SingletonDemo {private static SingletonDemo instance; Private SingletonDemo(){} public static synchronized SingletonDemo getInstance(){ instance=new SingletonDemo(); } return instance; Private constructor() {companion object {private var instance: SingletonDemo? = null get() { if (field == null) { field = SingletonDemo() } return field } @Synchronized fun get(): SingletonDemo{ return instance!! }}}Copy the code

We all know that there are thread-safety issues with lazy locking, and that you need to use Synchronized locks. In Kotlin, if you want to declare a method as Synchronized, you need to add @synchronized annotations.

Double check lock type

Public class SingletonDemo {private volatile static SingletonDemo instance; private SingletonDemo(){} public static SingletonDemo getInstance(){ if(instance==null){ synchronized (SingletonDemo.class){ if(instance==null){ instance=new SingletonDemo(); } } } return instance; Private constructor() {companion object {val instance: SingletonDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { SingletonDemo() } }}Copy the code

A: wow! You guys are surprised no, not impressed. We implemented multiple lines of Java code in a few lines of code. We use Kotlin’s Lazy attribute.

Internal implementation of Lazy

public fun <T> lazy(mode: LazyThreadSafetyMode, initializer: () -> T): Lazy<T> =        when (mode) {            LazyThreadSafetyMode.SYNCHRONIZED -> SynchronizedLazyImpl(initializer)            LazyThreadSafetyMode.PUBLICATION -> SafePublicationLazyImpl(initializer)            LazyThreadSafetyMode.NONE -> UnsafeLazyImpl(initializer)        }Copy the code

Observe the code above, because our incoming mode = LazyThreadSafetyMode. SYNCHRONIZED, then directly go SynchronizedLazyImpl, we continue to observe SynchronizedLazyImpl.

Lazy interface

SynchronizedLazyImpl implements a Lazy interface.

Public interface Lazy<out T> {// When the object is instantiated, the object will not change. Public fun isInitialized(): Boolean} Public fun isInitialized(): Boolean}Copy the code

Continue to look at SynchronizedLazyImpl as follows:

Internal implementation of SynchronizedLazyImpl

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 val lock = lock ? : this override val value: T get() {val _v1 = _value; == 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 { val typedValue = initializer!! Initializer = null typedValue}} initializer = null typedValue} initializer = null typedValue}Copy the code

Using the code above, we see that SynchronizedLazyImpl overrides the value property of the Lazy interface and rewrites its property accessor. The logic is similar to Java’s double checking.

How does the SynchronizedLazyImpl object get the return value of the higher order function? . Again, delegate properties are involved.

The syntax for delegate properties is: val/var < attribute name >: < type > by < expression >. The expression after BY is the delegate, because the property’s corresponding get() (and set()) are delegated to its getValue() and setValue() methods. Property delegates do not have to implement any interface, but need to provide a getValue() function (and setValue() — for the var property).

The lazy. kt file declares the getValue extension function for the Lazy interface. This method is called when the final assignment is made.

@ kotlin. Internal. InlineOnly / / returns the value of initialization. public inline operator fun <T> Lazy<T>.getValue(thisRef: Any? , property: KProperty<*>): T = valueCopy the code

Static inner class

Private static class SingletonHolder{private static SingletonDemo instance=new SingletonDemo(); } private SingletonDemo(){ System.out.println("Singleton has loaded"); } public static SingletonDemo getInstance(){ return SingletonHolder.instance; SingletonDemo private constructor() {companion object {val instance = singletonholder.holder} private object SingletonHolder { val holder= SingletonDemo() }}Copy the code

There is nothing to say about the implementation of static inner classes. Kotlin and The Java implementation are basically the same.

supplement

After the end of this article, many friends have asked how to add an attribute to the singleton in Kotlin’s version of Double Check. Here I provide you with an implementation method. (Sorry, I just got around to it recently)

class SingletonDemo private constructor(private val property: Companion object {@volatile private var instance: SingletonDemo? = null fun getInstance(property: Int) = instance ? : synchronized(this) { instance ? : SingletonDemo(property).also { instance = it } } }}Copy the code

What about? The: operator if? : Returns the left-hand expression if the left-hand expression is not empty, otherwise returns the right-hand expression. Note that the right-hand side of the expression is evaluated if and only if the left-hand side is empty.

Kotlin intelligent type conversion

For conversions between child and parent classes

  • Let’s start with some Java code

Public class Student extends Person{public void study(){system.out.println (" I'm learning a new language Kotlin! ") ); }}public static void main(String[] args){ Person person = new Student(); if(person instanceof Student){ ((Student) person).study(); }}Copy the code
  • Wouldn’t it be unwise to cast the person object to Student even though the person object is typed in main?

  • The same situation is much simpler in Kotlin

fun main(args: Array<String>) {    val person: Person = Student()    if (person is Student) {        person.study()    }}Copy the code
  • In Kotlin, once the type is determined, you can call a function of a subclass directly from an object of the parent class

Safe type conversion

  • In the same example above, what if we didn’t do the type judgment and went straight to the strong cast?

public static void main(String[] args) { Person person = new Person(); ((Student) person).study(); }Copy the code
  • The result can only be the Exception in the thread “is the main” Java. Lang. ClassCastException

  • Is there a better solution in Kotlin?

val person: Person = Person()val student:Student? =person as? Studentprintln(student) // nullCopy the code
  • Add a? After the conversion operator? If the conversion fails, a NULL is returned

Intelligent conversions in empty types

  • You need to know about Kotlin type safety in advance (type safety in Kotlin (optimization of null Pointers))

val aString: String? = "Hello Kotlin"if (aString is String) {    println(aString.length)}Copy the code
  • AString is defined to be potentially null, the way we wrote it before, so we need to write it that way

val aString: String? = "Hello Kotlin"println(aString? .length)Copy the code
  • But we have checked whether it is a String, so it must not be empty, and we can print its length directly

T () – > Unit, () – > Unit

In kotlin development, it is common to see some system functions as arguments

public inline fun <T> T.apply(block: T.() -> Unit): T{    block()    return this}public inline fun apply(block: () -> Unit){    block()}Copy the code

T.()->Unit; T.()->Unit; T.()->Unit;

Thoughts on Kotlin and the Java Programming language After a month of development with Kotlin

Welcome to contribute ~