Comece a usar o Kotlin em 30 minutos

Discuta, complemente, corrija. Fonte de referência "Crazy Kotlin Lecture Notes" de Li Gang

1. Tipos básicos
2. Operadores 3.
Controle de processo
4. Funções e expressões lambda
5. Funções avançadas
6. Arrays e coleções
7. Orientado a objetos
8. Genéricos 9.
Sintaxe relativamente tendenciosa

1. Tipo básico

(1) Cada tipo possui uma extensão. Por exemplo, Int possui outro Int?, a diferença é que este último pode ser null quando definido, mas precisa ser chamado como ?.fun() ao chamar métodos relacionados, ou Chamada obrigatória para !!.fun(). Tudo isso é para maximizar a prevenção de NPE. ?.fun() é chamado apenas quando a variável não está vazia, portanto, geralmente apenas a chamada forçada acionará o NPE.
(2) A conversão entre valores, tipos como Int e String fornecem métodos para converter para outros tipos, como Long e Float, que é toXXX(), como Int to Float->toFloat(); (3) A definição é
variável Variável: var v: Int = 1 ou var v = 1, o compilador deduzirá automaticamente a variável e a primeira é recomendada; a variável imutável é val. (4) Não há tipo de objeto, substituído por Qualquer (
5
) Tipo de string: ① Compatível com caracteres de índice, str[1]; ② Compatível com variáveis ​​ou expressões incorporadas:

val price = 10
var str = "涨了${price}个点"
var str1 = "随机数:${rand.nextInt(10)}, str长度是${str.length}"

2. Operadores

(1) Operador de intervalo: intervalo fechado (para (i em 0...5), i de 0 a 5 (inclusive)), intervalo semi-fechado (para (i em 0 até 5 (exclusivo))), reverso fechado Interval (for(i in 5 downTo 0)), tamanho do passo do intervalo (5 down 0 passo 2, o tamanho do passo deve ser um número positivo), o intervalo de intervalo acima também pode ser atribuído a variáveis, por exemplo: var v = 0 ...5, v pode Simplesmente considerado como um LinkedList;
(2) Sobrecarga de operador: semelhante a C++, cálculos entre valores como adição, subtração, multiplicação e divisão e comparações de tamanho têm métodos correspondentes. Por exemplo, a+b é equivalente a a.plus(b), a >b é equivalente a a.compareTo(b)>0, a++ é equivalente a a.inc(), incluindo o operador de intervalo a...b é realmente equivalente para a.rangeTo(b). Exemplo simples, sobrecarregando ++:

// data class代表数据类,像C的struct
data class Data(val x : Int, val y : Int) {
    // 关键字是operator,还可重载双目运算符等。
    operator fun inc() : Data {
        return Data(x + 1, y + 1)
    }
}

3. Controle de processo

(1) Existe um açúcar gramatical especial, que pode atribuir diretamente uma variável através do controle do processo, como:

// v是一个String或者是个Int
var v = if (b) {
    print("true")
    "这变量单独一行,直接会赋值给v"
} else {
    // 这个100也可以没有,这样这个分支返回的就是一个Any对象
    100
}

Observe que o valor de retorno da função não pode ser retornado dessa maneira, ou return;
(2) o while da instrução de loop é o mesmo que java, mas o for é diferente, **Não existe for(int i = 0; i < 10; i++)** Sim, em vez disso, use passagem de intervalo: for(i in 0 até 10)
(3) Não há sintaxe switch , substituída por when ... -> instrução:

// param是个Any类型,语句只有一行用->就行了,多行用{},is可要可不要
when(param) {
    is Float -> {
        print("是Float")
    }
    10 - 8 -> print("值是2") 
    in 0..10 -> print("在0到10内")
    'd','e' -> print("字符d或e")
    is Data -> print("是Data对象")
    else -> print("不知道是什么")
}

Quando a instrução apresenta: ①O valor após a ramificação não precisa ser uma constante, pode ser qualquer expressão; ②Corresponde primeiro, executa primeiro e depois quebra após a execução, e não julgará a ramificação seguinte, que é diferente da opção; ③A o exemplo acima param é um tipo Any, tantos tipos podem ser julgados. Se o compilador deduz o tipo, como param = 1, foi deduzido que é um Int, então a ramificação está relacionada apenas a Int; ④ Nada corresponde , vá para a outra ramificação.

4. Funções e expressões lambda

(1) Formato gramatical:
fun function name() : [retorna o tipo de valor] { // corpo do código}
(2) retorna o tipo vazio void e substitui-o por Unit;
(3) função de expressão única: a função retorna apenas uma única expressão , isso Você pode omitir as chaves e especificar o corpo da função após o sinal de igual (=):
fun test(x : Int, y : Float) : Float = x * y
(4) Parâmetros nomeados: como python, como teste (10, y = 1f), o x frontal usa parâmetros posicionais e o último y usa parâmetros nomeados.Se você deseja misturar parâmetros nomeados e parâmetros posicionais ao chamar a função, os parâmetros nomeados devem ser colocados após os parâmetros posicionais; (5) Valor padrão dos parâmetros formais: fun test(x
: Int = 1) {}
(6) Parâmetros variáveis, modificados com vararg: fun test(vararg x : Int) {}, uma função só pode ter um parâmetro variável em na maioria, se o parâmetro variável estiver na frente, os seguintes parâmetros devem ser Use parâmetros nomeados.
(7) Função inline: use modificação inline, o mesmo que C++;
(8) expressão lambda, sintaxe padrão:
{(lista formal de parâmetros) - >
// zero a várias instruções executáveis
​​}
Exemplo:

var square = { n : Int ->
    n * n    
}
print(square(5)) //输出25

(9) A essência de retorno da função anônima ainda é uma função, return é usado para retornar a própria função e o retorno da expressão lambda é usado para retornar a função em que está.

5. Funções de ordem superior

(1) O tipo de função é exatamente como o tipo de dados, que pode ser usado para definir variáveis, também pode ser usado como um tipo de parâmetro de função e também pode ser usado como um tipo de valor de retorno de função, como um ponteiro de função C. O tipo de função de cada função é composto pela lista de parâmetros formais da função, -> e o tipo de valor de retorno, por exemplo:
fun foo(a : Int, name : String) : String O tipo de função desta função é:
( Int, String) -> String, se for um parâmetro vazio ou valor de retorno vazio:
fun test(), então: ()->Unit ou (), por exemplo:

fun pow(base : Int, expo : Int) : String {}
// 使用::将函数名称赋值给变量
var myfun : (Int, Int)->String = ::pow 

(2) Como parâmetro de função:
fun test(fn : (Int)->Int) {}
(3) Como valor de retorno de função:
fun test() : (Int)->String {}

6. Matrizes e coleções

(1) A diferença entre arrays é relativamente grande, e a criação de tipos básicos de arrays é alterada para usar objetos como BooleanArray, IntArray, etc.; para
outros tipos, use os métodos arrayOf(), arrayOfNulls(), emptyArray() e Array(tamanho: Int, init: ( Int)->T):

// 创建包含指定元素的数组,相当于java数组的静态初始化,知道元素类型用intArrayOf也是一样
var arr1 = arrayOf(2, 4, 10)
// 相当于new int[10]
var arr2 = IntArray(5)
// 或者使用arrayOfNulls:
var arr3 = arrayOfNulls<Double>
// 使用lambda表达式初始化,相当于new int[]{1, 4, 9}
var intArr4 = IntArray(3, {it * it})

(2) Coleções, Kotlin realmente não implementa nenhuma classe de coleção, apenas empresta as classes de coleção originais da estrutura de coleção Java, as coleções Kotlin são divididas em duas categorias: coleções variáveis ​​e coleções imutáveis. XXXOf está "na frente" Coleções imutáveis
, "behind" são coleções mutáveis, como listOf(), listOfNotNull(), mapOf(), setOf() são todas coleções imutáveis, arrayListOf, hashMapOf(), linkedSetOf() são todas coleções mutáveis, diretamente Também é possível usar coleções java. Por exemplo, criar um array diretamente no arquivo kotlin ArrayList() também é
muito útil para coleções de mapas. Exemplo:

var map = mapOf("java" to 89, "kotlin" to 32)
map["spring"] = 90
for (en in map.entries)
for (key in map.keys)
for ((key, value) in map)
print("java" in map) // 输出true

(3) Métodos comuns de arrays e coleções:
= "distinct(): remove elementos repetidos no array
= "indices(): return IntRange[0, arr.length);
= "withIndex(): retorna um objeto iterador, o Todos os elementos do objeto são IndexValue, que podem acessar o índice e o valor do elemento ao mesmo tempo:

for((index, value) in arr.withIndex()) {
    print("索引${index},值${value}")
}

="all(predict:(T)->Boolean), use a expressão lambda para julgar se todos os elementos satisfazem a previsão; =" any
(predict:(T)->Boolean), semelhante a todos, desde que um elemento seja satisfeito , return true;
= "count(predict:(T)->Boolean), semelhante a todos, retorna o número que satisfaz a previsão
= "contentToString(): equivalente ao método Arrays.toString em java;
= "drop | dropLast (n : Int), remova vários elementos antes ou depois da matriz;
= "dropWhile | dropLastWhile(predicado:(T)->Boolean), remova elementos de acordo com a condição de previsão até que o primeiro elemento antes ou atrás não seja mais satisfeito; = "first
| last(predicate:(T)->Boolean), obtenha o primeiro elemento que satisfaça a previsão antes ou depois, existem dois métodos correspondentes para obter o índice, indexOfFirst e indexOfLast; = "filterXX(), findXX( ), uma
série Métodos para filtrar e encontrar elementos

Sete. Orientado a objetos

(1) O formato padrão da classe:

[修饰符] class 类名 [constructor 主构造器] {
    零个到多个次构造器...
    属性,方法...
}

A palavra-chave do construtor pode ser omitida se o construtor primário não tiver nenhuma anotação ou modificador.
(2) O modificador open open é um antônimo de final e é usado para modificar uma classe, método ou atributo, indicando que a classe pode derivar subclasses, e o método ou atributo pode ser reescrito.
(3) Kotlin fornecerá métodos getter e setter padrão para propriedades de leitura/gravação e métodos getter padrão para propriedades somente leitura; (4) A
principal diferença entre a sintaxe das funções de nível superior (e níveis de classe) e a sintaxe de métodos em classes são modificadores In: funções de nível superior não podem usar modificadores protegidos, abstratos e finais, mas métodos em classes podem usar public|protected|internal|private|final|abstract|open esses modificadores.
(5) Kotlin usa val para definir propriedades somente leitura e var para definir propriedades read-write. O sistema irá gerar métodos getter para propriedades somente leitura (val) da classe e gerar métodos getter e setter para read escrever propriedades (var). Ao usar a "sintaxe de ponto", os métodos setter e getter são realmente chamados e você também pode fazer algumas operações adicionais:

class Demo() {
    var full
        get() = full
        set(value) {
            print("invoke")
            full = value
        }
}

Essas propriedades são públicas e finais por padrão (não podem ser herdadas)
(6) Propriedades de bastidores: modifique as propriedades com private e o Kotlin não gerará nenhum método getter e setter para propriedades de bastidores. Portanto, o programa não pode acessar diretamente as propriedades dos bastidores, e o desenvolvedor deve fornecer métodos getter e setter para as propriedades dos bastidores. (7)
Kotlin requer que todas as propriedades sejam explicitamente inicializadas pelo programador—ou atribuir o valor inicial ao declarar a propriedade; ou Para atribuir um valor inicial à propriedade no contêiner, o Kotlin fornece o modificador lateinit para resolver a inicialização atrasada da propriedade. Atributos modificados com lateinit não podem especificar um valor inicial ao definir o atributo nem no construtor (usar isso está sujeito ao risco de NPE):

lateinit var name : String

Restrições: ① apenas propriedades variáveis ​​podem ser modificadas; ② propriedades modificadas não podem ter métodos getter e setter personalizados; ③ propriedades modificadas não podem ser propriedades nativas (as propriedades correspondentes aos 8 tipos básicos de java, como Int); ④ propriedades modificadas Type
( 8) A instrução import do Kotlin suporta a palavra-chave as, de forma que um alias pode ser especificado para a classe importada (como o pacote import do python), resolvendo assim o problema de importar classes com o mesmo nome em dois pacotes diferentes. Kotlin cancela os direitos de acesso padrão do Java (direitos de acesso ao pacote) e introduz caracteres de controle de acesso interno (direitos de acesso ao módulo). Kotlin cancela os direitos de acesso do pacote protegido.
(9) Os parâmetros do construtor principal do Kotlin podem ser usados ​​na inicialização do objeto e também podem ser usados ​​ao declarar propriedades. O construtor principal é mais como o bloco de inicialização do Java, ou o bloco de inicialização. O bloco de inicialização não pode passar parâmetros, e o Kotlin permite que os parâmetros sejam passados ​​para o bloco de inicialização por meio do design do construtor principal. Bloco de inicialização do objeto:

class Person(name : String = "狗娃") {
    // 里面可以访问对象属性,初始化块可以有多个,构造器的属性可以有默认值
    init {
        var a = 6
        print("name : ${name}")
    }
}

(10) Kotlin exige que todos os construtores secundários sejam delegados para chamar o construtor primário , ou seja, o construtor primário deve ser chamado primeiro (o código no bloco de inicialização é executado) e, em seguida, o código do construtor secundário é executado. (Se apenas um construtor for definido, então é o construtor principal, e o construtor principal não foi feito especialmente para seguir o nome da classe):

class Person(name : String) {
    init {
        //调用了次级构造器之前都会先进来这里
    }
    // 带两个参数的次构造器
    constructor(name : String, age : Int) : this(name){}
}

(11) Herança de classe:

修饰符 class Sub : Fa{}

A classe Any é a classe pai de todas as classes. É um pouco como Object, mas não é equivalente. A classe Any possui apenas três métodos: equals(), hashCode() e toString(). Classes Kotlin, incluindo atributos, não podem ser derivadas de subclasses por padrão. Todas são modificadas com final, e o modificador open precisa ser adicionado para ser herdado.
(12) A subclasse herda a classe pai, e o construtor da classe pai deve ser chamado primeiro, e a palavra-chave super pode ser usada para chamar, ou a palavra-chave this é chamada primeiro e outros construtores são chamados. De qualquer forma, o construtor da classe pai é finalmente chamado.
(13) Substituir o método ou propriedade da classe pai, usando o modificador override
(14) Se você precisar usar super na subclasse para se referir aos membros do supertipo, você pode usar colchetes angulares mais o
nome do supertipo qualificado super para referência . Por exemplo, herdando uma classe Bar e implementando uma interface, todos eles possuem um método test com o mesmo nome (o método de interface do Kotlin pode ter entidades), e você pode forçar a chamada de super<Bar>.test() ao chamar. existe apenas um, você não precisa dele Colchetes angulares;
(15) Kotlin fornece operadores de verificação de tipo: is e !is, usados ​​como instância java. Existe outro símbolo obrigatório de transformação como, que não é seguro. Recomenda-se usar como?. Se a transformação falhar, nenhuma exceção ocorrerá, mas nulo será retornado. ( 16) Kotlin's this é mais poderoso que Java's this,
e Kotlin's this suporta o formulário "@ Class name", que limita o objeto de qual classe isso representa.
(17) Uma grande diferença entre Kotlin e Java é: o modificador final de Kotlin não pode modificar variáveis ​​locais, e open não pode modificar variáveis ​​locais. Use const para modificar constantes que podem executar "substituição de macro", como c++: const val i = 1
(18) Uma classe modificada com selado é chamada de classe selada, que é uma classe abstrata especial usada especialmente para derivar subclasses. A diferença entre uma classe selada e uma classe abstrata comum é que a subclasse da classe selada é fixa. A subclasse direta da classe lacrada deve estar no mesmo arquivo que a própria classe lacrada, e a classe lacrada não pode ser derivada de outros arquivos, e a subclasse indireta não está sujeita a essa restrição.
(20) Semelhante ao Java 8, a interface do Kotlin pode conter métodos abstratos e métodos não abstratos, bem como herança múltipla, mas não instâncias. Se o atributo correspondente não tiver nenhum método setter ou getter, o modificador abstrato será adicionado automaticamente ao atributo. As interfaces também suportam herança múltipla.
(21) Classe aninhada (equivalente à classe interna estática): a classe definida na classe também pode ser colocada na função da classe; classe interna
(classe interna não estática): uma classe aninhada decorada com interior.
(22) Kotlin O modificador estático foi cancelado. Exceto para classes aninhadas, todos os membros em classes Kotlin são membros não estáticos. Kotlin também permite que classes aninhadas sejam definidas em interfaces, mas classes internas não podem ser definidas em interfaces. (23)
Substituir classe interna anônima : expressão de objeto, uma classe interna anônima pode especificar apenas um tipo pai (interface ou classe pai), mas uma expressão de objeto pode especificar 0~N tipos pai (interface ou classe pai):

object [: 0~N个父类型] {}
// java是直接new Thread(){},kotlin就变成:
object : Thread() {}

(24) Declaração de objeto e singleton, declaração de objeto e expressão de objeto são muito semelhantes, não os confunda:

object ObjectName[: 0~N个父类型]{}
//这个MyObject直接是一个对象了,有点相当于 val MyObejct = object : Thread {}
//因为不能赋值,也不需要再var,所以这样生成的对象就是一个单例了
object MyObject : Thread{}

A diferença entre uma declaração de objeto e uma expressão de objeto: ① Uma expressão de objeto é uma expressão que pode ser atribuída a uma variável; ② Uma declaração de objeto pode conter classes aninhadas, mas não pode conter classes internas, enquanto uma expressão de objeto pode inversamente... ; ③ Declarações de objeto não podem ser definidas em (25) Objetos complementares e membros estáticos em funções e métodos
: Declarações de objetos modificados por complementares, cada classe pode definir apenas um objeto complementar e os membros do objeto complementar da classe podem ser chamados diretamente:

class Demo {
    companion object myObjct : Thread{
        var name = ""
        fun test(){}
    }
}
Demo.test()
print(Demo.name)

Objetos complementares também podem ser estendidos.
(26) A classe de enumeração de Kotlin pode definir métodos abstratos;
(27) A essência da delegação de classes e atributos é delegar alguns métodos que precisam ser implementados por esta classe para outros objetos - equivalente a tomar emprestados os métodos de outros objetos como sua própria implementação , através da palavra-chave:

interface TestInterface{
    fun test()
    var name : String
}
class TestImpl : TestInterface {
    override fun test(){ //...}
    override var name = ""
}
// 定义一个类实现TestInterface,委托对象t
class Test2Impl(t : TestImpl) : TestInterface by t
// 另外一种写法,直接生成一个TestImpl作为委托对象
class Test3Impl : TestInterface by TestImpl() {
    // 属性委托
    var name : String by TestImpl()
}

Oito. Genéricos

(1) O uso básico de genéricos é semelhante ao de java. A diferença é que os parâmetros genéricos devem especificar o tipo claramente. Por exemplo, o seguinte está errado:

var a = ArrayList()

Além disso, ao herdar uma classe genérica, a subclasse não pode mais ser genérica;
(2) genéricos java não suportam alteração de tipo, java usa curingas para resolver esse problema e kotlin usa alteração segura de tipo em vez de curingas java, o limite superior do curinga kotlin (covariante genérica fora) significa que é seguro retirar (excluir) objetos dele, enquanto os objetos recebidos (in) tornam-se não confiáveis ​​e o limite inferior (variável genérica in) é o contrário, simplesmente Digamos, Array<in Int> é equivalente ao limite inferior genérico de Java Array<?super Int>, e extends está fora.
(3) Por exemplo, Collection<? extends Number> é equivalente a Collection<out Number>, e os elementos extraídos dela são pelo menos uma subclasse de Number, mas não se sabe se a referência real dentro dele é Collection<Int> ou Collection<Float> , adicionar elementos a ele torna-se não confiável;
(4) Usar in e out para modificar genéricos é uma mudança de tipo na declaração. Usar in para modificar, então genéricos só podem aparecer nos parâmetros formais do método, enquanto out só pode aparecer no valor de retorno do método. Se você quiser usar genéricos tanto no parâmetro formal quanto no valor de retorno, não use in e out para modificá-lo. Esse tipo de modificação sem in e out pertence ao tipo de uso. O típico é a classe Array<T>;
(5) "*" projeção de asterisco é para lidar com tipos primitivos de java:

var list : ArrayList<*> = arrayListOf(1, "kotlin")

(6) As funções genéricas são iguais às do java. Os genéricos do Kotlin têm um limite superior padrão: Algum? Apenas um limite superior pode ser especificado entre colchetes angulares. Se você precisar especificar mais de um, precisará usar a instrução where e a classe genérica também é usada:

class A<T> where T : Comparable<T>, T : Cloneable {}

Nove. Gramática relativamente tendenciosa

(1) Tipo alias, como C typedef, typealias type alias = tipo existente, como typealias myInt = Int
(2) Passe em parâmetros variáveis, use *:

fun test(vararg x : Int, y : Float) {}

val arr = arrayOf(1, 2, 3)
test(*arr, y = 1f)

(3) Função recursiva de cauda, ​​quando a função chama a si mesma como a última linha de código que executa e não há mais código após a chamada recursiva, a sintaxe recursiva de cauda pode ser usada. A recursão de cauda não pode ser usada em blocos try, catch e, finalmente, de tratamento de exceção. As funções recursivas de cauda precisam ser modificadas com tailrec:

tailrec fun test(n : Int, total : Int = 1) : Int {
    if (n == 1) {
        return total
    } else {
        return test(n - 1, total * n)
    }
}

(4) Função local: A função definida na função é ocultada de fora e só pode ser usada dentro da função, que é aplicável à lógica mais complexa dentro da função; (5) Se a expressão lambda tiver apenas um parâmetro
formal , então este parâmetro formal pode ser omitido , o código o usa para substituir o nome do parâmetro formal:
var square : (Int)->Int = {it * it}
(6) expressões lambda têm a mesma sintaxe de fechamento que gradle, se o último parâmetro da função é o tipo de função , permite que expressões lambda sejam especificadas fora dos parênteses se passadas como seu argumento:

var list = listOf(1, 2, 3)
var rt = list.dropwhile(){it > 1} // rt 就是[2,3]

(7) As expressões lambda têm uma falha grave: o tipo de valor de retorno não pode ser especificado. Se o compilador não puder inferir o tipo de valor de retorno, funções comuns ou funções anônimas podem ser usadas neste momento:

fun(el) : Boolean {
    return Math.abs(el) > 20
}

Três características: ①Em comparação com funções comuns, o nome da função é removido; ②Se o sistema inferir o tipo de parâmetro formal da função anônima, então a função anônima permite que o tipo de parâmetro formal seja omitido; ③Como Lambda, pode especificar o valor de retorno para compensar isso Um defeito de ;
(8) expressões lambda, funções anônimas e funções locais podem capturar variáveis ​​e constantes no contexto:

fun out() {
    var list = mutableListOf<String>()
    fun inner() {
        list.add("局部函数inner能访问到其上下文的变量list")
    }
}

(9) A série de arrays ou coleções do método AssociateXX é um pouco como o método flatMap de Rxjava
foto
(10) Os métodos na classe podem ser independentes. Observe que, ao extrair, o primeiro parâmetro é o tipo de objeto ao qual pertence a função, por exemplo, o método run abaixo não possui parâmetros formais, mas também precisa receber um parâmetro formal Dog para indicar o objeto aceito na chamada.

var rn : (Cat)->Unit = Cat::run
val d = Cat()
rn(d)

É muito parecido com uma propriedade de C++, esqueci como se chama...:
(11) método componentN e desconstrução, Kotlin permite que N propriedades de um objeto sejam "desconstruídas" para múltiplas variáveis:

// 将user对象的两个属性分别赋值给n、p两个变量
var (n, p) = user
// 等价于
var n = user.component1()
var p = user.component2()

Se o programa quiser desconstruir o objeto em várias variáveis, ele precisa definir vários métodos componentN() para a classe, e o método precisa ser decorado com o operador, que será atribuído às variáveis ​​em ordem. Se um determinado atributo for não for necessário, use um espaço reservado "_":

// 构造器使用var修饰的形参,直接就成为了该类的一个属性
class User(var name : String, var pass : String) {
    operator fun component1() { return name}
    operator fun component2() { return pass}
}
// 占位符使用
var (_, pass) = user

Como o mapa pode ser percorrido como for((chave, valor) no mapa), ele usa a desconstrução
(12). Usando a sintaxe da desconstrução, a função pode retornar vários valores. Para simplificar a implementação da desconstrução, o Kotlin fornece um Classe: classe de dados, classe modificada com dados, irá gerar automaticamente o método componentN():

data class User(var name : String, var pass : String)

Requisitos: ① O construtor principal deve ter pelo menos um parâmetro; ② Todos os parâmetros do construtor principal devem ser declarados como atributos com val ou var; ③ Não pode ser modificado com abstrato, aberto ou selado.
(13) A biblioteca padrão Kotlin fornece duas classes de dados, Pair e Triple, a primeira armazena dois dados e a última armazena três dados.
(14) Notação infix, o método modificado com infix é um pouco como um operador binocular, que só pode ter um parâmetro:

class Apples() {
    infix fun add(num : Int) {
    }
}
var a = Apples()
val otherApples = a add Apples()

(15) A expressão Lambda do Kotlin também pode usar desconstrução, desde que seu parâmetro seja um tipo que suporte desconstrução;
(16) Kotlin permite métodos de extensão para classes:

var p = Person()
fun Person.extraFun() { print("Person类的扩展方法") }
p.extraFun()

A extensão realmente não modifica a classe estendida, a classe estendida ainda é a classe original sem nenhuma alteração. A essência é definir uma função. Quando um programa usa um objeto para chamar um método de extensão, o Kotlin realizará uma análise estática ao compilar. É encontrar a função de extensão de acordo com o objeto de chamada e o nome do método e convertê-la em uma função chamar. Se a subclasse e a classe pai estenderem o mesmo nome de função, o método estendido pela subclasse não precisará usar a palavra-chave override.
(17) Se uma classe contiver um método membro e um método de extensão com a mesma assinatura, quando o programa chamar esse método, o sistema sempre executará o método membro em vez do método de extensão. Ordem de pesquisa: ①Se é definido; ②Se é estendido
(18) Além das funções de extensão, as funções anônimas também podem ser estendidas e os atributos também podem ser estendidos. Os atributos estendidos não podem ter valores iniciais e os métodos setter e getter devem ser fornecidos ; (19)
Kotlin fornece uma função lazy() que usa uma expressão Lambda como argumento e retorna um objeto Lazy. Os objetos Lazy só podem ser usados ​​como objetos delegados para propriedades somente leitura. A lógica de processamento do método getValue() do Lazy é: ao chamar esse método pela primeira vez, o programa calculará a expressão Lambda e obterá seu valor de retorno e, em seguida, o programa chamará este método novamente, a expressão Lambda não é mais calculada, mas o valor de retorno obtido no primeiro cálculo é usado diretamente. Objetos preguiçosos só podem ser usados ​​como objetos delegados para propriedades somente leitura:

val lazyString : String by Lazy {
    print("第一次调用")
    "狗娃"
}
print(lazyString)
print(lazyString) // 这次不会再调用lazy里面的代码了

(20) Há também uma sintaxe para monitoramento de atributos.

Acho que você gosta

Origin blog.csdn.net/aa642531/article/details/88379968
Recomendado
Clasificación