Basic data types

digital

Type Bit Width
Double 64
Float 32
Long 32
Int 32
Short 16
Byte 8

The literal constants

  • Decimal number: 123
    • The Long type is marked with a capital L: 123L
  • Hexadecimal :0x0F
  • Binary: 0 b0001011
  • Default Double:123.5, 123.5e10
  • Float is marked with f or f: 123.5F

Underscores in numeric literals (since 1.1)

val oneMillion = 1 _000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010
Copy the code

Note:

  1. There is no implicit extension conversion for numbers (e.gJavaIn theintWe can implicitly convert tolong)
  2. kotlinOctal is not supported
  3. KotlinMiddle characters are not numbers

character

Characters are represented by the type Char. They can’t be treated as numbers

fun check(c: Char) {
    if (c == 1) { // Error: type incompatible
        / /...}}Copy the code

Each number type supports the following conversion

  • toByte(): Byte
  • toShort(): Short
  • toInt(): Int
  • toLong(): Long
  • toFloat(): Float
  • toDouble(): Double
  • toChar(): Char

Boolean

Boolean is represented by Boolean type and has two values: true and false. Empty reference booleans will be boxed if needed. The built-in Boolean operations are:

  • | | – logical or short circuit
  • && — Short circuit logic and
  • ! Non – logic

An array of

The control flow

If expression

In Kotlin, if is an expression, that is, it returns a value. So there is no need for ternary operators (condition? Then: otherwise), because plain if would do the job.

// Traditional usage
var max = a 
if (a < b) max = b

// With else 
var max: Int
if (a > b) {
    max = a
} else {
    max = b
}
 
// as an expression
val max = if (a > b) a else b
Copy the code

An if branch can be a block of code, with the final expression as the value of that block:

val max = if (a > b) {
    print("Choose a")
    a
} else {
    print("Choose b")
    b
}
Copy the code

If you use if as an expression rather than a statement (for example, returning its value or assigning it to a variable), the expression needs to have an else branch.

when

When replaces the Java switch operator. Its simplest form is as follows

when (x) {
    1 -> print("x == 1")
    2 -> print("x == 2")
    else- > {// Notice this block
        print("x is neither 1 nor 2")}}Copy the code

Like if as an expression, when can be used as either an expression or a statement. If when is used as an expression, there must be an else branch, unless the compiler can detect that all possible cases have been covered.

The while loop

while (x > 0) {
    x--
}

do {
  val y = retrieveData()
} while(y ! =null) // y is visible here
Copy the code

In the loopBreakwithcontinue

  • returnThe default is to return from the function that most directly surrounds it or from an anonymous function.
  • breakTerminate the loop that most directly surrounds it.
  • continueContinue the next cycle that most directly surrounds it.

function

Declaration of functions

Functions in Kotlin are declared using the fun keyword:

fun double(x: Int): Int {
    return 2 * x
}
Copy the code

Define a function

  1. A function that takes two Int arguments and returns Int:
fun sum(a: Int, b: Int): Int {
    return a + b
}
Copy the code
  1. Functions that automatically infer expressions as function bodies and return value types:
fun sum(a: Int, b: Int) = a + b
Copy the code
  1. Function returns a meaningless value,UnitIt’s a type that has only one valueUnitThe type of. This value does not need to be returned explicitly:
fun printSum(a: Int, b: Int): Unit {
    println("sum of $a and $b is ${a + b}")}Copy the code

Note:

  1. When a function has a large number of arguments or default arguments, the function can be called by naming the arguments.
  2. When a function call mixes positional and named arguments, all positional arguments precede the first named argument
  3. In the callJavaFunction cannot use named parameter syntax becauseJavaBytecode does not always reserve the names of function arguments;

Function scope

In Kotlin functions can be declared at the top of a file, without the need to create a class to hold a function as Java does. In addition to top-level functions, functions in Kotlin can also be declared in local scopes, as member functions, and as extension functions.

Local function

Kotlin supports local functions, where one function is inside another function:

fun count(a){
    var count =0;
    fun innerCount(a){
        print(count)
    }
}
Copy the code

Local functions can access local variables of external functions (that is, closures).

A member function

A member function is a function defined inside a class or object:

class Sample(a){
    fun foo(a) { print("Foo")}}Copy the code

Member functions are called in dot notation:

Sample().foo() // Create an instance of class Sample and call foo
Copy the code

Generic function

Functions can have generic arguments, specified by using Angle brackets before the function name:

Fun <T> List(item: T): List<T> {...... }Copy the code

Inline function

Extension function

Extend the new functionality of a class without inheriting the class or using any type of design pattern like decorator. To declare an extension function, we need to prefix it with a receiver type, that is, the type to be extended.

Higher-order functions and Lambda expressions

Higher-order functions are functions that use functions as arguments or return values.

Function types

  • All function types have A list of parameter types enclosed in parentheses and A return type :(A, B) -> C represents A function type that takes A and B arguments and returns A value of type C. The argument type list can be empty, for example () -> A. The Unit return type cannot be omitted.
  • Function types can have an additional receiver type, which is specified before the dot in the notation: type A.(B) -> C represents A function that can be called on A receiver object of TYPE A with A type B argument and return A value of type C. Functional literals with receivers are usually used with these types.
  • Suspended functions are a special class of function types that have one representationsuspendModifier, for examplesuspend () -> Unitorsuspend A.(B) -> CCoroutine correlation.

Function type notation optionally includes function parameter names: (x: Int, y: Int) -> Point. These names can be used to indicate the meaning of the parameters.

Function type instantiation

  • A code block that uses a function numeric value, in one of the following forms:
    • Lambda expressions:{ a, b -> a + b }.
    • Anonymous functions:fun(s: String): Int { return s.toIntOrNull() ?: 0 }

Function literals with receivers can be used as values of function types with receivers

  • Callable references with existing declarations:
    • Top-level, local, member, extension function:: : isOdd, String: : toInt.
    • Top-level, member, extended properties:List<Int>::size.
    • Constructor:::Regex

This includes callable references to bindings that point to specific instance members: foo::toString

  • Example of a custom class that implements a function-type interface:
class IntTransformer: (Int) -> Int {
    override operator fun invoke(x: Int): Int = TODO()
}

val intFunction: (Int) -> Int = IntTransformer()
Copy the code

With sufficient information, the compiler can infer the function type of the variable:

val a = { i: Int -> i + 1 }
Copy the code

Function types with and without a receiver are interchangeable with non-literals, where the receiver can substitute for the first argument, and vice versa. For example, values of type (A, B) -> C can be passed or assigned to places where A.(B) -> C is expected, and vice versa:

val repeatFun: String.(Int) -> String = { times -> this.repeat(times) }
val twoParameters: (String, Int) -> String = repeatFun // OK

fun runTransformation(f: (String, Int) -> String): String {
    return f("hello".3)
}
val result = runTransformation(repeatFun) // OK
Copy the code

Note that function types without receivers are inferred by default, even though variables are initialized by extending function references. If you want to change this, specify the variable type explicitly.

Lambda expressions

Lambda expressions and anonymous functions are functionally literal, i.e., functions that are not declared but are immediately passed as expressions. Consider the following example:

max(strings, { a, b -> a.length < b.length })
Copy the code

The function Max is a higher-order function that takes a function as a second argument. Its second argument is an expression that is itself a function, the function literal, equivalent to the following named function:

fun compare(a: String, b: String): Boolean = a.length < b.length
Copy the code
Lambda expression syntax

The full syntax of a Lambda expression is as follows:

val sum = { x: Int, y: Int -> x + y }
Copy the code

Lambda expressions are always enclosed in curly braces, full syntactic parameter declarations are enclosed in curly braces, with optional type annotations, and the function body is followed by a -> symbol. If the inferred return type of the lambda is not Unit, then the last (or possibly single) expression in the body of the lambda is treated as the return value. If we left out all the optional annotations, it would look like this:

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

Passing a lambda expression to the last argument: In Kotlin there is a convention that if the last argument to a function accepts the function, the lambda expression passed as the corresponding argument can be placed outside the parentheses:

val product = items.fold(1) { acc, e -> acc * e }
Copy the code

If the lambda expression is the only argument when called, the parentheses can be omitted entirely:

run { println("...")}Copy the code

Tail recursive function

Functions can have generic arguments, specified by using Angle brackets before the function name:

Fun <T> List(item: T): List<T> {...... }Copy the code

Classes and objects

The members of the class

  • Constructor and initializer block
  • function
  • attribute
  • Nested and inner classes
  • Object statement

The constructor

Kotlin uses the keyword class to declare classes

class Invoice {... }Copy the code

On the JVM, if all arguments to the main constructor have default values, the compiler generates an additional no-parameter constructor

Properties and Fields

  1. Attributes can use keywordsvarDeclare mutable, otherwise use read-only keywordsval.
    1. The use of read-only attributesvalBegan to replacevar
    2. Read-only attributes are not allowedsetter. To use an attribute, you simply refer to it by name
  2. Complete syntax for declaring an attribute:
var <propertyName>[: <PropertyType>] [= <property_initializer>]
    [<getter>]
    [<setter>]
Copy the code
  1. getterAlways have the same visibility as the property,setterVisible permissions cannot be higher than properties.
  2. privateClass attributes are not generated by defaultgetter / setter, all access to it is direct access, once you have customgetter / setterWhen you visit, you have to go throughgetter / setter. (You can view it in the generated Java file)

Behind the field

What are the behind the scenes fields?

There is a corresponding field in the JVM class attribute. You can view the files generated in the JVM by going to Android Studio’s Tools -> Kotlin -> Show Kotlin Bytecode -> Decompile.

In the following case, there is no background field

val isEmpty: Boolean
    get(a) = this.size == 0
Copy the code

This case is compiled directly without the isEmpty field:

public final boolean isEmpty(a) {
      return this.size == 0;
   }
Copy the code

If there are fields behind the scenes:

  1. If at least one accessor for a property uses the default implementation, or if a custom accessor references a background field through field, a background field will be generated for that property. In particular, the private property does not have a default getter/setter but does have a hidden field.

  2. The field property is used in the custom getter/setter, so there must be a field behind it. This field is the “key” we use to access the field behind the scenes. Similar to it in a Lambda expression, it is not a real keyword, but has a special meaning in a particular statement and is not a keyword in any other statement.

Behind the properties

The use of behind-the-scenes attributes

Most of the time, we want to define attributes like this:

  1. Externally, the property is val, which can only be read but not written.

  2. Inside a class, it is represented as a var attribute, meaning that its value can only be changed inside the class.

val size get(a) = _size
private var _size:Int = 0
Copy the code

Corresponding Java code

private int _size;
public final int getSize(a) {
    return this._size;
}
Copy the code

This _size property is behind the scenes

Compiler constant

Properties of known values can be marked as compile-time constants using the const modifier. These attributes need to satisfy the following conditions

  • On the top floor orThe object statementcompanion objectA member of
  • In order toStringOr a value of a native type
  • No customizationgetter

inheritance

In Kotlin all classes have a common superclass, Any, which is the default superclass for classes without a supertype declaration:

Inline class

Nested classes

Seal type

Data classes

Object expressions and object declarations

Object expression

We need to create an object of a class that has made slight changes to a class without explicitly declaring a new subclass for it, which Java handles with anonymous inner classes.

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

Multiple supertypes can be specified by a comma-separated list followed by a colon. If we need “just one object” and no special supertype, we can simply write:

fun foo(a) {
    val adHoc = object {
        var x: Int = 0
        var y: Int = 0
    }
    print(adHoc.x + adHoc.y)
}
Copy the code

Note that anonymous objects can be used as types declared only in local and private scopes. If you use an anonymous object as the return type of a public function or as the type of a public property, the actual type of the function or property will be the supertype declared by the anonymous object, or Any if you don’t declare Any. Members added to anonymous objects will not be accessible

class C {
    // Private function, so its return type is an anonymous object type
    private fun foo(a) = object {
        val x: String = "x"
    }

    // Public function, so its return type is Any
    fun publicFoo(a) = object {
        val x: String = "x"
    }

    fun bar(a) {
        val x1 = foo().x        / / no problem
        val x2 = publicFoo().x  // error: failed to parse reference "x"}}Copy the code

Just like Java anonymous inner classes, code in an object expression can access variables from the scope that contains it. (Unlike Java, this is not limited to final variables.)

fun countClicks(window: JComponent) {
    var clickCount = 0
    var enterCount = 0

    window.addMouseListener(object : MouseAdapter() {
        override fun mouseClicked(e: MouseEvent) {
            clickCount++
        }

        override fun mouseEntered(e: MouseEvent) {
            enterCount++
        }
    })
    / /...
}
Copy the code

Object statement

The singleton pattern is useful in some scenarios, and Kotlin (after Scala) makes singleton declarations easy:

object DataProviderManager {
    fun registerDataProvider(provider: DataProvider) {
        / /...
    }

    val allDataProviders: Collection<DataProvider>
        get(a) = / /...
}
Copy the code

Object declarations cannot be locally scoped (that is, nested directly inside a function), but they can be nested inside other object declarations or non-inner classes. This is called object declarations. And it always follows the object keyword with a name. Like variable declarations, object declarations are not expressions and cannot be used on the right side of assignment statements.

The initialization of object declarations is thread-safe.

To reference the object, we simply use its name:

DataProviderManager. RegisterDataProvider (...)Copy the code

These objects can have supertypes:

object DefaultListener : MouseAdapter() {
    override fun mouseClicked(e: MouseEvent) { …… }

    override fun mouseEntered(e: MouseEvent) {... }}Copy the code

Associated object

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

class MyClass {
    companion object Factory {
        fun create(a): 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.Companion
Copy the code

The name of the class used by itself (not a qualifier of another name) can be used to refer to the companion objects of the class (whether named or not) :

class MyClass1 {
    companion object Named { }
}

val x = MyClass1

class MyClass2 {
    companion object { }
}

val y = MyClass2
Copy the code

Note that even though the members of the companion object look like static members of another language, they are still instance members of the real object at run time, and, for example, interfaces can be implemented:

interface Factory<T> {
    fun create(a): T
}

class MyClass {
    companion object : Factory<MyClass> {
        override fun create(a): MyClass = MyClass()
    }
}

val f: Factory<MyClass> = MyClass
Copy the code

Semantic differences 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).

interface

Kotlin’s interface is similar to Java 8 in that it contains both declarations of abstract methods and implementations. Unlike abstract classes, interfaces cannot hold state. It can have attributes but must be declared abstract or provide accessor implementations.

interface MyInterface {
    fun bar(a)
    fun foo(a) {
      // Optional method body}}Copy the code

attribute

Properties declared in an interface are either abstract or provide an implementation of the accessor. Properties declared in an interface cannot have a backing field, so accessors declared in the interface cannot reference them. As we explained in the background field above, you must customize the getter for the var property and the setter for the val property.

interface MyInterface {
    val prop: Int

    val propertyWithImplementation: String
        get(a) = "foo"

    var prop2:Int

    var propWithGetAndSet:String
        get(a) = "prop2"
        set(value) {
            print(value)
        }

    fun foo(a) {
        print(prop)
    }
}


Copy the code

Inheritance of interfaces

Unlike Java, where a class or object can implement one or more interfaces, only the missing implementation is defined.

interface MyInterface2:MyInterface{
    fun pritlnProps(){
        println("propertyWithImplementation = "+propertyWithImplementation)
        println("propWithGetAndSet = "+propWithGetAndSet)
        println("prop = "+prop)
        println("prop2 = "+prop2)
    }
}

class A :MyInterface,MyInterface2{
    override val prop: Int = 1

    override var prop2: Int = 2

}
Copy the code

Resolving coverage Conflicts

Consistent with the override rules for class inheritance, the super keyword is required to specify which supertype to inherit from.

Refer to the link

  • Background fields and background properties
  • Scope functions Scope Funtions