草庐IT

Kotlin机制总结

JakeWharton 2023-09-13 原文
什么是kotlin?

kotlin是静态类型的编程语言,运行于jvm之上。如果在编译时知道变量的类型,则语言是静态类型的,在运行时知道变量类型,则语言是动态类型。

什么是extension(扩展)函数

Kotlin 可以对一个类的属性和方法进行扩展,对被扩展的类代码本身不会造成任何影响。
扩展函数可以为已经存在的类添加新的方法,并且不会修改原来的类。

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

       User("libo", 10).login()
    }

    fun User.login() {
        Log.i("TAG","去登录")
    }
}

data class User(var name: String, var age: Int)
在kotlin中有多少种构造函数

kotlin将构造函数分为了两种:主构造函数和次构造函数。

//主构造方法如下,跟在类名后面
class Person constructor(name:String){
    
}
//无参主构造方法
class Person constructor(){
    
}
//当主构造方法没有任何注解或者可见性修饰符时,可以省略,写成下面这样
class Person {
    
}
class Person {
    
    /**
     * 无参次构造方法
     */
    constructor(){

    }
    /**
     * 有参次构造方法
     */
    constructor(name:String){

    }
}

主构造函数没有函数体,如果我们想在主构造函数中写一些逻辑,怎么办呢,kotlin给我们提供一个init结构体,所有构造函数中的逻辑都可以写在里面:

class Person(val name: String, val age: Int) : Person() {
    init {
        println("name is $name")
        println("age is $age")
    }
}
  • lateinit和by lazy的区别

Kotlin 基于 Java 的空指针提出了一个空安全的概念,即每个属性默认不可为null。 在某个类中,如果某些成员变量没办法在一开始就初始化,并且又不想使用可空类型(也就是带?的类型)。那么,可以使用lateinit或者by lazy来修饰它。

1.lateinit 只能用于修饰变量 var,不能用于可空的属性和 Java 的基本类型。
2.lateinit 可以在任何位置初始化并且可以初始化多次。
3.lazy 只能用于修饰常量 val,并且 lazy 是线程安全的。
4.lazy 在第一次被调用时就被初始化,以后调用该属性会返回之前的结果。

  • 高阶函数

如果一个函数接收另一个函数作为参数,或者返回类型是一个函数,那么这个函数我们就称之为高阶函数。

举例如下,这些函数用法与Rxjava同名函数类似

        //forEach,用于遍历集合
        val arr = intArrayOf(1, 2, 4, 6)
        arr.forEach {
            println(it)
        }

         //map 返回一个每一个元素根据给定的函数转换所组成的List
        val arr = intArrayOf(1, 2, 4, 6)
        val newArr = arr.map { (it * 2).toString()  }
        println(newArr)

        //flatMap  遍历所有的元素 ,为每一个创建一个集合 ,最后把所有的集合放在一个集合中
        val arr = intArrayOf(1, 2, 4, 6)
        val arr2 = intArrayOf(10, 39, 39, 18, 88)
        var arr3 = intArrayOf(100, 200, 383, 198)
        arrayListOf(arr, arr2, arr3).flatMap { iterator ->
            iterator.map {
                println(it.toString())
            }
        }
        
        //filter,用于过滤数据
        val arr = intArrayOf(1, 2, 7, 6, 10, 39, 39, 18, 88)
        arr.filter {
            //这里是通过条件
            it % 2 == 0
        }

        //takeWhile,带满足条件的过滤
        //它的实现和filter不同地方在filter总是会遍历当前IntArray的所有元素,而takeWhile在第一次发现元素不满足的时候就不再遍历
        val arr = intArrayOf(1, 2, 4, 6, 8, 9, 10, 12, 14)
        arr.takeWhile {
            it % 2 == 0
        }

        //take/takeLast
        val arr = intArrayOf(1, 2, 4, 6, 8, 9, 10, 12, 14)
        var list = arr.take(5)  //取前5个
        var list2 = arr.takeLast(3)  //取后3个
  • 伴生对象的总结

类似于 Java 中使用类访问静态成员的语法。因为 Kotlin 取消了 static 关键字,所以 Kotlin 引入伴生对象来弥补没有静态成员的不足。可见,伴生对象的主要作用就是为其所在的外部类模拟静态成员。

每个类可以最多有一个半生对象;
使用 const 关键字修饰常量,类似于 Java 中的 static final修饰。
可以使用 @JvmField 和 @JvmStatic 类似于 Java 中调用静态属性和静态方法;
伴生对象可以扩展属性和扩展方法。

  • init代码块和构造方法以及伴生对象中代码的调用时机

创建Person类,创建person对象打印方法调用时机:

class Person {
    private var name: String = "jack"
    constructor() {
        println("constructor 方法调用")
    }
    init {
        println("init 方法调用")
    }
    companion object {
        init {
            println("companion init 1")
        }
    }
}

从Tools-->kotlin-->show Kotlin Bytecode,将Person类反编译成java类得到:


伴生对象转为了静态代码块,init代码块插入到了构造方法的开头处。静态代码块在编译期运行,然后依次运行构造方法的代码。打印的结构为:

结论:伴生对象先于init方法,再先于构造方法。首先伴生对象中的代码是在类加载时就会执行。init代码块中的方法会按顺序放在主构造函数中,主构造函数中原来的代码会在后面执行。

  • const和val有什么区别?

所述const关键字被用于声明那些不可变在本质即,这些属性是只读属性的属性。
但是,这些属性的值必须仅在编译时已知,这const就是也称为编译时常量的原因,相当于java中的static final修饰。该val关键字还用于只读属性。但是const和之间的主要区别在于val,val属性也可以在运行时进行初始化,即不可变变量。

  • Kotlin data类机制

创建Result类:

data class Result(var code:Int,val msg:String)

转成java类,知道为什么Kotlin开发强大了吧。

public final class Result {
   private int code;
   @NotNull
   private final String msg;
    //...省略setter和getter方法
   public Result(int code, @NotNull String msg) {
      Intrinsics.checkNotNullParameter(msg, "msg");
      super();
      this.code = code;
      this.msg = msg;
   }

   public final int component1() {
      return this.code;
   }

   @NotNull
   public final String component2() {
      return this.msg;
   }

   @NotNull
   public final Result copy(int code, @NotNull String msg) {
      Intrinsics.checkNotNullParameter(msg, "msg");
      return new Result(code, msg);
   }

   @NotNull
   public String toString() {
      return "Result(code=" + this.code + ", msg=" + this.msg + ")";
   }

   public int hashCode() {
      int var10000 = this.code * 31;
      String var10001 = this.msg;
      return var10000 + (var10001 != null ? var10001.hashCode() : 0);
   }

   public boolean equals(@Nullable Object var1) {
      if (this != var1) {
         if (var1 instanceof Result) {
            Result var2 = (Result)var1;
            if (this.code == var2.code && Intrinsics.areEqual(this.msg, var2.msg)) {
               return true;
            }
         }

         return false;
      } else {
         return true;
      }
   }

系统自动为数据类生成哪些内容:

  • 自动生成了变量的get、set方法

  • 生成equals/hashCode的方法。

  • 自动重写toString方法返回形如:”User(name=guojingbu,age=18)“的字符串

  • 生成copy()方法,方便完成对象复制。

  • 如果变量是val修饰,只会生成get方法。

  • 什么是Range操作符?

Range是Kotlin相对Java新增的一种表达式,它表示的是值的范围,类似于数学中的区间。
Range的表达式是像这样子的:1..20,其中..是运算符,它表示一个闭区间[1, 20]。

有关Kotlin机制总结的更多相关文章

  1. SPI接收数据异常问题总结 - 2

    SPI接收数据左移一位问题目录SPI接收数据左移一位问题一、问题描述二、问题分析三、探究原理四、经验总结最近在工作在学习调试SPI的过程中遇到一个问题——接收数据整体向左移了一位(1bit)。SPI数据收发是数据交换,因此接收数据时从第二个字节开始才是有效数据,也就是数据整体向右移一个字节(1byte)。请教前辈之后也没有得到解决,通过在网上查阅前人经验终于解决问题,所以写一个避坑经验总结。实际背景:MCU与一款芯片使用spi通信,MCU作为主机,芯片作为从机。这款芯片采用的是它规定的六线SPI,多了两根线:RDY和INT,这样从机就可以主动请求主机给主机发送数据了。一、问题描述根据从机芯片手

  2. Simulink方法总结和避坑指南(一)——Simulink入门与基本调试方法 - 2

    文章目录一、项目场景二、基本模块原理与调试方法分析——信源部分:三、信号处理部分和显示部分:四、基本的通信链路搭建:四、特殊模块:interpretedMATLABfunction:五、总结和坑点提醒一、项目场景  最近一个任务是使用simulink搭建一个MIMO串扰消除的链路,并用实际收到的数据进行测试,在搭建的过程中也遇到了不少的问题(当然这比vivado里面的debug好不知道多少倍)。准备趁着这个机会,先以一个很基本的通信链路对simulink基础和相关的debug方法进行总结。  在本篇中,主要记录simulink的基本原理和基本的SISO通信传输链路(QPSK方式),计划在下篇记

  3. 【动态规划】背包问题(详细总结,很全) - 2

    【动态规划】一、背包问题1.背包问题总结1)动规四部曲:2)递推公式总结:3)遍历顺序总结:2.01背包1)二维dp数组代码实现2)一维dp数组代码实现3.完全背包代码实现4.多重背包代码实现一、背包问题1.背包问题总结暴力的解法是指数级别的时间复杂度。进而才需要动态规划的解法来进行优化!背包问题是动态规划(DynamicPlanning)里的非常重要的一部分,关于几种常见的背包,其关系如下:在解决背包问题的时候,我们通常都是按照如下五部来逐步分析,把这五部都搞透了,算是对动规来理解深入了。1)动规四部曲:(1)确定dp数组及其下标的含义(2)确定递推公式(3)dp数组的初始化(4)确定遍历顺

  4. ruby - Ruby 是否提供响应 OS X 上的 Apple 事件的机制? - 2

    我正在使用Ruby-Tk为OSX开发一个桌面应用程序,我想为该应用程序提供一个AppleEvents接口(interface)。这意味着应用程序将定义它将响应的AppleScript命令的字典(对应于发送到应用程序的Apple事件),并且用户/其他应用程序可以使用AppleScript命令编写Ruby-Tk应用程序的脚本。其他脚本语言支持此类功能——Python通过位于http://appscript.svn.sourceforge.net/viewvc/appscript/py-aemreceive/的py-aemreceive库和Tcl通过位于http://tclae.source

  5. ruby - Ruby 的方法解除绑定(bind)机制有什么意义? - 2

    Method#unbind返回对该方法的UnboundMethod引用,稍后可以使用UnboundMethod#bind将其绑定(bind)到另一个对象.classFooattr_reader:bazdefinitialize(baz)@baz=bazendendclassBardefinitialize(baz)@baz=bazendendf=Foo.new(:test1)g=Foo.new(:test2)h=Bar.new(:test3)f.method(:baz).unbind.bind(g).call#=>:test2f.method(:baz).unbind.bind(h).

  6. 相机面试问题总结 - 2

    1,Camera基本工作原理答案:光线通过镜头Lens进入摄像头内部,然后经过IRFilter过滤红外光,最后到达sensor(传感器),senor分为按照材质可以分为CMOS和CCD两种,可以将光学信号转换为电信号,再通过内部的ADC电路转换为数字信号,然后传输给DSP(如果有的话,如果没有则以DVP的方式传送数据到基带芯片baseband,此时的数据格式RawData,后面有讲进行加工)加工处理,转换成RGB、YUV等格式输出。数据流是如何从sensor到APP的?上述描述结束后,在ISP处理后面的阶段,数据会进行分流,分为capture,preview,video等以供后续动作使用。例如

  7. 【结构与算法】—— 数据结构代码总结 | 数据结构代码大全 - 2

    📢博客主页:https://blog.csdn.net/dxt19980308📢欢迎点赞👍收藏⭐留言📝如有错误敬请指正!📢本文由肩匣与橘编写,首发于CSDN🙉📢生活依旧是美好而又温柔的,你也是✨目录🔴线性表1.1顺序表1.1.1顺序表定义1.1.2顺序表基本操作1.2单链表1.2.1单链表节点定义1.2.2单链表基本操作1.3双链表1.3.1双链表节点定义1.3.2双链表基本操作1.4静态链表🟠栈和队列2.1栈2.1.1顺序栈2.1.2链式栈2.2队列2.2.1顺序队列2.2.2链式队列2.3应用🟡串3.1串的定义与实现3.2串的模式匹配🟢树与二叉树4.1二叉树4.1.1二叉树的概念4.1.2

  8. Selenium等待机制之显示等待 - 2

    显示等待需要用到两个类:WebDriverWait和expected_conditions两个类WebDriverWait:指定轮询间隔、超时时间等expected_conditions:指定了很多条件函数(也可以自定义条件函数)具体可以参考官网:selenium.webdriver.support.expected_conditions—Selenium4.5documentationfromseleniumimportwebdriverfromselenium.webdriver.common.byimportByfromselenium.webdriver.support.uiimpor

  9. MyBatisPlus总结 - 2

    目录MyBatisPlusMP特点MP框架结构MP使用准备导入依赖springboot整合mybatisplus配置文件定义好实体类User后编辑mapper接口@Mapper与@MapperScan("包名")区别MP基本操作新增操作删除操作通过id删除用户通过map作为条件删除通过多个id实现删除更新用户通过id进行用户更新查询用户 根据id查询用户根据多个id查询用户根据map集合作为条件查询用户通用Service接口一些操作 查询总记录数批量添加数据MP常用注解雪花算法前言垂直分表水平分表条件构造器继承结构使用条件构造器实现查询操作查询所有用户根据构造器查询主键字段集合根据条件构造器查

  10. Kotlin:通过并消耗2个参数函数? - 2

    我正在尝试在Kotlin学习功能编程,并且难以使此代码起作用:importjava.util.*funcaseName(br:String,c:Int):String{if(c==0){returnbr.toLowerCase()}else{returnbr.toUpperCase()}}funmapIt(ns:ArrayList,f:(String,Int)->String):List{valcoll:List=ns.map{it->f(it,_)}returncoll}funmain(args:Array){valnames=arrayListOf("Joe","Bill","Murrar

随机推荐