The modifier

Access modifier
The modifier Members of the relevant commentary
final Can’t be rewritten Class members used by default
open Can be rewritten What needs to be clarified
abstract Must be rewritten Used in abstract classes
override Overrides a member of a parent class or interface If there is no final declaration, the overridden member defaults to open

You can subclass any class in Java and override any method unless final is declared. Changes to a base class can result in improper behavior by subclasses, namely vulnerable base class problems. Effective Java advises “either design and document inheritance, or disallow it.” Kotlin adopts this philosophy, where classes and methods in Java are open by default, whereas in Kotlin classes and methods are final by default.

open class RichButtion:Clickable{
    // Default is final and cannot be overridden
    fun disable(a){}
    / / the open can be rewritten
    open fun animate(a){}
    The override method defaults to open
    override fun click(a){}}Copy the code

Interfaces and abstract classes default to open, and their abstract members default to open

abstract class Animate{
    // The default is open
    abstract fun animate(a)
    // Non-abstract methods default to final
    fun animateTwice(a){}}Copy the code
Visibility modifier
The modifier Members of the class The top statement
Public (default) Visible everywhere Visible everywhere
internal Visible in module Visible in module
protected Visible in subclasses
private Seen in class Visible in the file

The default visibility in Java – package private – is not present in Kotlin. Kotlin only uses packages as a way to organize code in a namespace, not as visibility control. Instead, Koltin uses a new modifier, internal, which means “visible only within modules.” The advantage of internal is that it provides a true encapsulation of module implementation details.

interface

Interfaces contain definitions of abstract methods and implementations of non-abstract methods, but neither of them can contain any state.

interface Clickable {
    // backing-field is not supported and cannot store values
    var clickable: Boolean
    // Default open, can be overridden
    fun click(a)
    // Default final, cannot be overridden
    fun showOff(a) = println("I'm Clickable")}Copy the code

Since Koltin 1.0 was designed for Java 6, it does not support default methods in interfaces. Instead, it compiles the interface of each default method into a combination of a normal interface and a class that uses the method body as a static function.

public interface Clickable {
   boolean getClickable(a);
   void setClickable(boolean var1);
   void click(a);
   void showOff(a);
 
   public static final class DefaultImpls {
      public static void showOff(Clickable $this) {
         String var1 = "I'm Clickable";
         boolean var2 = false; System.out.println(var1); }}}Copy the code

The constructor

The Kotlin constructor has been partially modified relative to Java to distinguish between primary and slave constructors. The code in the initialization block actually becomes part of the main constructor. The delegate to the primary constructor is the first statement of the secondary constructor, so all code in the initialization block is executed before the secondary constructor body.

class Person {
	init {
		println("Init block")}constructor(i: Int) {
		println("Constructor")}}Copy the code

In most scenarios, class constructors are very concise: they either have no arguments, or they associate the arguments directly with the corresponding attributes

class User(val nickname:String,val isSubscribed:Boolean=false)
Copy the code

If the class has a primary constructor, each slave constructor needs to delegate to the primary constructor, either directly or indirectly.

class User(val nickname: String) {
    var isSubscribed: Boolean? =null
    constructor(_nickname: String, _isSubscribed: Boolean) : this(_nickname) {
        this.isSubscribed = _isSubscribed
    }
}
Copy the code

If the class has a parent class, should the constructor of the parent class be explicitly called

//Clickable is an interface with no constructor
class Buttion:Clickable{

}
// Display the call to the parent constructor even if there are no arguments
class RiseButton:Button(){
    
}
// If there are multilevel constructors, the super keyword can be invoked using the superclass constructor
class MyButton: View {
    constructor(ctx:Context):super(ctx)
    constructor(ctx: Context,attributes: AttributeSet?) :super(ctx,attributes)
}
Copy the code
Inner class, nested class, Sealed class, data class
Inner and nested classes

Nested classes in Kotlin cannot access instances of external classes, similar to Java static inner classes; The inner class in Kotlin requires the inner keyword to access instances of the outer class.

class Outer {
    private val bar: Int = 1
    / / inner classes
    inner class Inner {
        fun foo(a) = bar
    }
}
class Outer2 {
    private val bar: Int = 1
    // A nested class that does not hold references to external classes
    class Nested {
        fun foo(a) = 2}}val demo = Outer().Inner().foo() / / = = 1
val demo2 = Outer2.Nested().foo() / / = = 2
Copy the code
Seal type

Sealed classes are used to represent a restricted class inheritance structure: when a value is of a finite set of types and cannot have any other types. In a sense, they are an extension of enumerated classes: the collection of values of enumerated types is also limited, but only one instance of each enumerated constant exists, whereas a subclass of a sealed class can have multiple instances of containable state.

sealed class Expr
data class Const(val number: Double) : Expr()
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
Copy the code

A sealed class is abstract in itself; it cannot be instantiated directly and can have abstract members. Sealed classes are not allowed to have non-private constructors (their constructors default to private). Note that classes (indirect successors) of extended sealed class subclasses can be placed anywhere without being in the same file.

Data classes

Create classes that only hold data. In these classes, some standard functions are often mechanically derived from data. In Kotlin, this is called a data class and labeled data:

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

The compiler automatically exports the following members from all properties declared in the main constructor:

  • equals() / hashCode()Right;
  • toString()Format is “User(name=John, age=42)”;
  • componentN()Functions correspond to all attributes in declarative order;
  • The copy () function.

To ensure consistent and meaningful behavior in generated code, data classes must meet the following requirements:

  • The main constructor needs to take at least one argument;
  • All arguments to the main constructor need to be marked with val or var;
  • Data classes cannot be abstract, open, sealed, or internal;
Properties and Fields

The full syntax for declaring an attribute is:

var <propertyName>[: <PropertyType>] [= <property_initializer>]
[<getter>]
[<setter>]
Copy the code

Initializers, getters, and setters are optional.

The syntax of a read-only attribute differs from that of a mutable attribute in two ways:

  • Read-only attribute withval, while mutable attributes usevarThe statement
  • Read-only attributes are not allowedsettermethods

The default attribute declaration is:

var name: String = "Kotlin"
        get() = field
        set(value) {
            field = value
        }
Copy the code
The Object keyword

The Object keyword defines a class and creates an entity:

  • Object declaration: How a singleton is defined
  • Associated objects: can hold factory methods and other related classes
  • Object expression: substituteJavaAnonymous inner class of

There is an important semantic difference between object expressions and object declarations:

  • Object expressions are executed (and initialized) immediately where they are used;
  • Object declarations are lazily initialized when they are first accessed;
  • The initialization of the associated object matches the semantics of the Java static initializer when the corresponding class is loaded (parsed).
Object statement

Object declarations combine the declaration of a class with the declaration of a single instance of that class. Unlike an instance of a normal class, an object declaration creates the instance when it is defined.

object PayRoll {
    val allEmployees = arrayListOf<Person>()

    fun calculateSalary(a){}}Copy the code

You can decompile to see:

The object declaration is compiled into a class that holds its single INSTANCE through a static field, always named INSTANCE

public final class PayRoll {
   @NotNull
   private static final ArrayList allEmployees;
   public static final PayRoll INSTANCE;

   @NotNull
   public final ArrayList getAllEmployees() {
      return allEmployees;
   }

   public final void calculateSalary() {
   }
	// Constructor is private
   private PayRoll() {
   }

   static {
      PayRoll var0 = new PayRoll();
       // Static code block initializes instance object
      INSTANCE = var0;
      boolean var1 = false; allEmployees = new ArrayList(); }}Copy the code
Associated object

The Java static keyword is not part of Kotlin. Instead, Kotlin relies on function and object declarations at the package level, but the top-level function cannot access the private members of the class. Instead, it needs to write a function that can be called without a class instance but needs to access the inner members of the class.

fun getFacebookName(accountId: Int) = "fb:$accountId"

class User private constructor(val nickname: String) {
    companion object {
        fun newSubscribingUser(email: String) =
            User(email.substringBefore(The '@'))

        fun newFacebookUser(accountId: Int) =
            User(getFacebookName(accountId))
    }
}

fun main(args: Array<String>) {
    val subscribingUser = User.newSubscribingUser("[email protected]")
    val facebookUser = User.newFacebookUser(4)
    println(subscribingUser.nickname)
}
Copy the code

Associated objects, as normal objects, can also implement interfaces and extend functions and properties

data class Person(val name: String) {
    object NameComparator : Comparator<Person> {
        override fun compare(p1: Person, p2: Person): Int =
            p1.name.compareTo(p2.name)
    }
}

class Person(val firstname:String,val lastname:String){
    companion object{
        / /... Can be empty, but not omitted}}fun Person.Companion.fromJson(json:String):String{
    return json.substring(4)}Copy the code
Object expression

Object can be used to declare not only singletons but also anonymous objects, replacing the use of Java inner classes

fab.setOnClickListener(
     object : View.OnClickListener {
     	override fun onClick(view: View?). {
         //....}})Copy the code

Of course, you can also store it in a variable:

val listener = object : View.OnClickListener {
    override fun onClick(p0: View?). {
       //....}}Copy the code

Whereas Java anonymous inner classes can only extend one class or implement one interface, Kotlin’s anonymous objects can implement multiple interfaces or implement different interfaces.

extension

Kotlin can extend the new functionality of a class without having to inherit the class or use design patterns like decorator. This is done through special declarations called extensions.

Extension function

To declare an extension function, we need to prefix it with a receiver type, that is, the type to be extended.

fun MutableList<Int>.swap(index1: Int, index2: Int) {
    // The this keyword corresponds to the receiver object inside the extension function
    val tmp = this[index1] // "this" should list
    this[index1] = this[index2]
    this[index2] = tmp
}
Copy the code
  • Extensions don’t really modify the classes they extend. By defining an extension, you are not inserting a new member into a class, but simply calling the new function with a dot expression using a variable of that type.
  • Extension functions are statically distributed, that is, they are not virtual methods based on the receiver type. This means that the extension function called is determined by the type of the expression on which the function is called, not by the result of the expression’s run-time evaluation
  • If a class definition has a member function and an extension function, and the two functions have the same receiver type, the same name, and both apply to a given parameter, the case always takes the member function.
Extended attributes

Similar to functions, Kotlin supports extended attributes:

val <T> List<T>.lastIndex: Int
	get() = size - 1
Copy the code

Since the extension does not actually insert the member into the class, the background field is not valid for the extension attribute. This is why extended properties cannot have initializers. Their behavior can only be defined by explicitly provided getters/setters.

val House.number = 1 // Error: Extended attributes cannot have initializers
Copy the code

In fact, extended properties are compiled into getter/setter methods, but without fields:

public final class KtKt {
   public static final int getLastIndex(@NotNull List $this$lastIndex) {
      Intrinsics.checkParameterIsNotNull($this$lastIndex, "$this$lastIndex");
      return $this$lastIndex.size() - 1; }}Copy the code
entrust

The delegate pattern has proven to be a good alternative to implementing inheritance, and Kotlin can support it natively with zero boilerplate code.

Commissioned by class

You can delegate the implementation of an interface to another object using the **by keyword ** :

class CountingSet<T>(
        val innerSet: MutableCollection<T> = HashSet<T>()
) : MutableCollection<T> by innerSet {

    var objectsAdded = 0

    override fun add(element: T): Boolean {
        objectsAdded++
        return innerSet.add(element)
    }

    override fun addAll(c: Collection<T>): Boolean {
        objectsAdded += c.size
        return innerSet.addAll(c)
    }
}
Copy the code
Entrusted property

Koltin supports the delegate attribute, which has the syntax:

class Foo{
    // Attribute P delegates its accessor logic to another Delegate object
    // Get the proxy object by evaluating the following expression
    var p:Type by Delegate()
}
// The compiler creates a hidden secondary property and initializes it with an instance of the delegate object to which the initial property P is delegated
class Foo{
    private val delegate = Delegate()
    var p: Type
    	set(value) = delegate.setValue(... ,value)get()=delegate.getvalue(...)
}

Copy the code

Delegate must have getValue and setValue methods, standard signature:

class Delegate {
    //thisRef: proxyed object
    //property: Description of the proxied object
	operator fun getValue(thisRef: Any? , property:KProperty< * >): String {
		return "$thisRef, thank you for delegating '${property.name}' to me!"
	}
	operator fun setValue(thisRef: Any? , property:KProperty<*>, value: String) {
		println("$value has been assigned to '${property.name}' in $thisRef.")}}Copy the code
The delay attribute Lazy

The backing-field technique implements the delay attribute, which is verbose:

data class Email(val name: String)
fun loadEmails(person: Person): List<Email> {
    return listOf()
}
class Person(val name: String) {
    //backing-filed Is used to store data
    private var _emails: List<Email>? = null
    // Access to read properties
    val emails: List<Email>
        get() {
            if (_emails == null) {
                _emails = loadEmails(this)}return_emails!! }}Copy the code

Lazy () is a function that takes a lambda and returns an instance of lazy that can be used as a delegate to implement the delay property: 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

class Person(val name: String) {
    val emails by lazy { 
        loadEmails(this)}}Copy the code

By default, the evaluation of lazy is synchronized: the value is evaluated in only one thread, and all threads see the same value. Initialization if entrusted by the synchronous lock is not required, so that multiple threads can perform at the same time, it will be LazyThreadSafetyMode. PUBLICATION passed as a parameter to lazy () function. Initialization and if you are sure will happen when used with attributes in the same thread, you can use LazyThreadSafetyMode. NONE mode: it will not have any thread safety guarantee and related expenses

Lazy implements the delay attribute.

class ObservableChangeProperty(
    val propName: String,
    var propValue: Int.val changeSupport: PropertyChangeSupport
) {
    fun getValue(a): Int = propValue
    fun setValue(newValue: Int) {
        val oldValue = propValue
        propValue = newValue
        changeSupport.firePropertyChange(propName, oldValue, newValue)
    }
}

open class PropertyChangeAware {
    protected val changeSupport = PropertyChangeSupport(this)
    fun addPropertyChangeListener(listener: PropertyChangeListener) {
        changeSupport.addPropertyChangeListener(listener)
    }
}

class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
    val _age = ObservableChangeProperty("age", age, changeSupport)
    var age: Int
        get() = _age.getValue()
        set(value) {
            _age.setValue(value)
        }

    val _salary = ObservableChangeProperty("salary", salary, changeSupport)
    var salary: Int
        get() = _salary.getValue()
        set(value) {
            _salary.setValue(value)
        }
}
Copy the code

If you change the ObservaleProperty method signature to fit Kotlin’s convention:

class ObservableChangeProperty(
    var propValue: Int.val changeSupport: PropertyChangeSupport
) {
    operator fun getValue(p: Person, prop: KProperty< * >): Int = propValue
    operator fun setValue(p: Person, prop: KProperty<*>, newValue: Int) {
        val oldValue = propValue
        propValue = newValue
        changeSupport.firePropertyChange(prop.name, oldValue, newValue)
    }
}
class Person(val name: String, age: Int, salary: Int) : PropertyChangeAware() {
    var age: Int by ObservableChangeProperty(age, changeSupport)
    val salary: Int by ObservableChangeProperty(salary, changeSupport)
}
Copy the code
Observable property Observable

Delegates. Observable () receives two parameters: initial value and handler when modified. This handler is called whenever we assign a property (executed after the assignment). If you want to be able to intercept an assignment and “reject” it, use vetoable() instead of Observable (). The handler passed to vetoable is called before the attribute is assigned a new value to take effect.

class Person(
    val name: String, age: Int, salary: Int
) :PropertyChangeAware(){
    private val observer = { prop: KProperty<*>, oldvalue: Int, newValue: Int ->
		 changeSupport.firePropertyChange(prop.name, oldValue, newValue)
    }
    var age: Int by Delegates.observable(age, observer)
    var salary: Int by Delegates.observable(salary, observer)
}
Copy the code
Store attributes in a map

Delegate properties are another common use for objects with a dynamically defined set of properties, sometimes called Expando.

class Person {
    private val _attributes = hashMapOf<String, String>()

    fun setAttribute(attrName: String, value: String) {
        _attributes[attrName] = value
    }

    val name: String
        get() = _attributes["name"]!!!!! }Copy the code

Delegate attributes are used to store values in the map

class Person {
    private val _attributes = hashMapOf<String, String>()

    fun setAttribute(attrName: String, value: String) {
        _attributes[attrName] = value
    }
    val name: String by _attributes
}
Copy the code
Requirements for delegate properties
  • For a read-only property (that is, declared by val), the delegate must provide a name namedgetValueThe function that takes the following parameters and returns the same type or subtype as the attribute:
    • thisRefMust be the same as the property owner type (for extended attributes, the extended type) or its superclass type;
    • property— Must be a typeKProperty<*>Or its supertype.
  • For a mutable property (that is, declared by var), the delegate must provide an additional property namedsetValueThe function that takes the following arguments:
    • thisRefMust be the same as the property owner type (for extended attributes, the extended type) or its superclass type;
    • property— Must be a typeKProperty<*>Or its supertype
    • newValueMust be of the same type or a subtype of the property.

The getValue() or/and setValue() functions can be provided by member functions of the delegate class or by extension functions. The latter is more convenient when you need to delegate properties to objects that do not originally provide these functions. Both functions need to be marked with the operator keyword.

A delegate class can implement either ReadOnlyProperty or ReadWriteProperty interfaces that contain the required operator methods.

interface ReadOnlyProperty<in R, out T> {
	operator fun getValue(thisRef: R, property: KProperty< * >): T
}
interface ReadWriteProperty<in R, T> {
	operator fun getValue(thisRef: R, property: KProperty< * >): T
	operator fun setValue(thisRef: R, property: KProperty<*>, value: T)
}
Copy the code