• Android kotlin在实战过程问题总结与开发技巧详解


    1、介绍

            目前Android开发中,分为两派,Java派和Kotlin派,Java是宇宙开发第一语言,地位一直处于领先位置,但是Java会出现一个空指针的情况,所以kotlin的出现,填补了java的一些缺陷,但是也导致一些人习惯了Java语言,在转kotlin的时候,会经常写成java字段。所以我接下来把kotlin在开发过程常见的关键字和一些用法进行介绍,以及一些错误如何处理,看完基本就能正常上手写kotlin项目。

    二、常见使用介绍

    似曾相识,就是不会。看完恍然大悟。接下来对高频用法和高频关键字进行介绍

    1、var 和val,变量声明

    1.1 var 是声明为常量,有读与写权限

            var a:Int=10,

            a=20

    1.2val 是声明为常量,相当于final,只能读,没有写权限

    val b:Int=10

    b=20//这行报错

    2、对象的创建

    var  chidl:Child

    如果直接使用报错

    var child=Child()直接new一个新的

    3、!!和?区别

    !!:如果有空指针会被抛出

    ?:如果当前对象是空指针,将不会执行后面的操作

    如下:

     4、"?"的使用

    1.var child:Child?=null

    这种表示child为null,


    如果你直接写成

    var child:Child=null

    直接报错

     变成的初始化可以这样

    var child:Child= Child()
    声明成Child的类型,然后初始化
    
    var child1=Child()

    直接初始化成child类型

    5、可变数组与不可变数组

    kotlin中,数组分为可变和不可变,可变是可以添加值,不可变就是声明后,就不能再add进去。

     可变素组正常都是mutable开头,常见的有如下:

    mutableListOf<>()
    mutableMapOf<>()
    mutableSetOf<>()

    在这边,var和val对对象的修饰不是那么明显,即使你申请为val,可变数组还是可以操作的

    6、静态类,关键字:object

    我们在Java中会使用静态变量通过static来修饰,kotlin在修饰静态变量直接用object修饰类

    object StaticMian {
    
        fun log() {
            MyLog.log("object is static")
        }
    
    }

    使用:StaticMian.log()

    如果设置类为静态,里面的方法都不需要再进行修饰,和其他类一样使用

    静态类的内部类也可以

    object DemoOut {
    
        object DemoIn{
    
            fun log(){
                MyLog.log("DemoIn")
            }
        }
    
        fun log(){
            MyLog.log("DemoOut")
        }
    
    
    }

    7、单利模式:

    1.Synchronized


    单利在Java作为工具类用的好多地方,kotlin的单利模式是通过在get方法中

    object StaticMian {
        private var mian: StaticMian? = null
        @get:Synchronized
        val instance: StaticMian?
            get() {
                synchronized(StaticMian::class.java) {
                    if (mian == null) {
                        mian = StaticMian
                    }
                }
                return mian
            }
    
    
        fun log() {
            MyLog.log("object is static")
        }
    
    }

    关于在对象下方的get(),可以参考我的另一篇:Kotlin语法详解与实践教程,区分JAVA以及如何闭坑

    修饰该对象是同步,只需要修饰该方法的get,指向Synchronized,和申明变量差不多

    @+get:+关键字

    var +变量名:+类型


    第二种单利写法:

    class TestSing {
    
        companion object {
            private var test: TestSing? = null;
    
    
            fun getInstance(): TestSing {
                synchronized(TestSing::class.java) {
                    if (test == null)
                        test = TestSing()
                }
    
                return test!!
            }
    
        }
    }
    

    8、常量const关键字用法

    const是修饰val的,val是只有读权限,如果用const来修饰,那就变成了一个常量,const 只允许在top-level级别和object,也就是是说当前类必须是通过object修饰的,否则不能使用

    而且const修饰的变量必须要被初始化。

    1.正确写法

    const val a:Int = 0;

    2.错误写法
    const val a:Int;

    3.const的出现:只能出现在静态类或者静态模块中,否则报错

    9、接口使用使用

    1.接口创建

    1. interface MyInterface {
    2. public fun change(msg: String): String;
    3. public fun add(a: Int, b: Int): Int {
    4. return a + b
    5. }
    6. }

    也是通过关键字interface 来修饰

    写法没有啥区别,唯独有争议的地方就是方法可以有方法体,有方法体只能当方法使用

    2.继承接口

    类和的继承和接口都是通过":"实现的

    如果接口中定义的方法有方法体,在接口继承将不会需要重写,已被定义为方法了,可以直接调用,也可以重写

    1. class MyTestDemo :MyInterface {
    2. override fun change(msg: String): String {
    3. return msg+"===change";
    4. }
    5. override fun add(a: Int, b: Int): Int {
    6. return super.add(a, b)
    7. }
    8. }
    9. fun main() {
    10. var demo=MyTestDemo()
    11. MyLog.log(demo.change("123"))
    12. MyLog.log(demo.add(2,3))
    13. }

    3.参数为接口,如果View的onclick实践

    1.直接和java写法一致:

    1. demo.setInterface(object : MyInterface {
    2. override fun change(msg: String) {
    3. }
    4. })

    2.lambda表达式:

    无参:直接{}在空号内写回调

    demo.setOnclick {
    
    }

    有参:直接将参数名按顺序列出来,指向方法体->

    demo.addOnDestinationChangedListener {controller,destination,arguments->
        
        toast("${destination.displayName}")
        
    }

    10、多构造器以及继承类的构造器传参

    非继承类构造器

    1. class MyConstrDemo() {
    2. constructor(name: String) : this() {
    3. }
    4. constructor(name: String,age:Int) : this() {
    5. }
    6. }
    7. fun main() {
    8. var demo1=MyConstrDemo()
    9. var demo2=MyConstrDemo("")
    10. var demo3=MyConstrDemo("",12)
    11. }

    默认构造器通过类名后面追加。如果多重载,通过关键字来完成constructor(),返回值就是默认构造器类型

    类名后带参数的构造器又叫默认构造器,如果在类的内部通过constructor定义构造器,返回值类型一定是this(),this就是类名后面接的默认构造器。

    继承类构造器:

    1. open class Parent(name: String = "parent") {
    2. }

    1. class MyConstrDemo(name:String):Parent(name) {
    2. constructor() : this("") {
    3. }
    4. constructor(name: String,age:Int) : this() {
    5. }
    6. }

    和单类一样,只是要传参给父类。其他写法和非继承一样

    11、lateinit 关键字

    lateinit 关键是需要对变量进行初始化,切变量属性是private类型,切用来修饰var

    在Android开发过程中,比较常见的就是lateinit var来定义view

    lateinit var text=findViewById(id)as TextView

     这种修饰是申请变量可以为null,一般简单类型不适合

    1.错误,这种直接报错

    lateinit var age:Int 

    同样,这样修饰过的变量不能去修改set()和get()方法

     可以为null和不可以的示例:

    lateinit var mp1:HashMap
     var mp2:HashMap

    12、强制转换 as

    as在kotlin中作为类型的强制转换,Java中  int a=(int) 100.123f

    kotlin: 

    对象类型

    a=b as Int

    常见的view初始化如下:

    var text=findViewById(id) asTextView

    13、构造器对象使用

    我们如果在kotlin中构造器中有对象变量,需要引用构造器变量,无须将构造器变量赋值到类的内部变量,可以直接通过var修饰完直接引用

    class DataMapConst(var map:HashMap?) 
    
    1. class DataMapConst(var map:HashMap?) {
    2. lateinit var mp1:HashMap
    3. constructor( msg:String):this(null){
    4. }
    5. fun log(){
    6. MyLog.log(map!!?.toString())
    7. }
    8. inner class Child{
    9. fun log(){
    10. MyLog.log(map!!?.toString())
    11. }
    12. }
    13. }
    14. fun main() {
    15. var map:HashMap?=null
    16. map= HashMap()
    17. DataMapConst("ssss")
    18. var demo=DataMapConst(map);
    19. map.put("one","1")
    20. map.put("two","1")
    21. map.put("three","1")
    22. demo.Child().log()
    23. }

    14、对象默认set()和get()使用

    kotlin的变量都提供了set()和get() 方法,但是lateinit修饰过的变量,无法修改set()和get()方法

     
    

    1. class People {
    2. var name: String=""
    3. set(value) {
    4. field=value+"=123"
    5. }
    6. get() {
    7. return field + "==modify"
    8. }
    9. var age: Int = 0
    10. }
    11. fun main() {
    12. var people=People()
    13. people.name="zhangshan"
    14. MyLog.log(people.name)
    15. }

    set(value):这个value是 val,只能读,不可修改

    get():需要一个return

    field:是变量自身的值,如果了解Java反射的,应该明白,field是class获取到的变量

    想了解Java反射,可以参考我的另一篇文字  Android Java反射与Proxy动态代理详解与使用基础篇(一)

    这样我们就能随意修改field值,以及返回值。这类似Java hook技术。同时,也破坏了数据原有的安全性,谨慎使用。

    15、companion关键字

    companion只能修饰object,且该object必须是内部类静态模块。
    

    常见的用法如下

    1. class TestSing {
    2. companion object {
    3.         //类的静态模块
    4. }
    5. }

    所以object有两种用法

    1.修饰类为静态内

    2.和companion搭档,companion object修饰非静态类的静态模块

    如果类为静态类,就不能这样使用,因为静态类,里面的方法和熟悉都可以直接使用,再修饰成静态模块就会报错。

    所以 :只能出现在非静态类中

    companion object{

    }

    这种用法最为非静态类中的静态模块

    16、open关键字

     kotlin中的类和方法都是无法直接被继承或者重写,类似Java中被修饰了,但是可以通过open来修饰就变成了开放型类和方法。

    1. open class Base {
    2. open fun log(){
    3. }
    4. }
    5. class TestBase :Base(){
    6. override fun log() {
    7. super.log()
    8. }
    9. }

    17、Any,Unit,Nothing,Void

    1.Any:相当于java中的object使用

    Unit:是一个object类,类似java中的void

    fun aa(): Unit {
        
    }

    Nothing:是一个有私有构造器的类,常作为定义异常抛出类使用

    fun  bb():Nothing=throw java.lang.Exception("error");
    

    也经常在继承提示作为TD使用

    1. @kotlin.internal.InlineOnly
    2. public inline fun TODO(reason: String): Nothing = throw NotImplementedError("An operation is not implemented: $reason")

    Void:作为空来使用,常作为返回一个空类型。

    fun cc(): Void? {
        var map:Any;
        return null;
    }
    

    上面四种类型,使用最多的是Any和Nothing,关于unit和void可以作为了解,不做参考

    18、inner:内部类修饰

    如果类中嵌套类,我们常叫成为内部类,在Java中直接写,但是kotlin中,如果需要使用内部类,需要用一个inner来修饰

    1. class People {
    2. inner class MySon {
    3. var name: String = ""
    4. fun log() {
    5. MyLog.log(name)
    6. }
    7. }
    8. }
    9. fun main() {
    10. var sun = People().MySon()
    11. sun.name="123"
    12. sun.log()
    13. }

       注意: 在使用过程中,内部类的使用如下

     正确:

    var sun = People().MySon()

    错误:

    var sun = People.MySon()

    19、when

    when的使用和Java中的Switch一个逻辑,但是格式右边

    1. val a = 10
    2. when (a) {
    3. 0, 10 -> {
    4. MyLog.log("0-10")
    5. }
    6. 20 -> {
    7. MyLog.log("==20")
    8. }
    9. 30 -> {
    10. MyLog.log("==30")
    11. }
    12. else -> {
    13. MyLog.log("not value")
    14. }
    15. }

    when( 表达式)

    0,10:a>=0&&a<=10

    相当于

    case 0:

    case10:

    break

    else相当于default

    注意:任何模块结束自带break。

    20.、is的使用

    在kotlin中,类型的判断是 is,和java instanceof一样

    1. var b=10
    2. if (b is Int)
    3. {
    4. MyLog.log("Int")
    5. }

    21、in在for中的使用

     in在for循环中使用比较多,和迭代去关联。但是,关于for的使用如下

    1.for (i in 1 until 100 )
    

      相当于 [1,100)的区间类,步长是1,相当于i ++

    2.for (i in 1 until 100 step 2)

    指定步长是多少,step跟上步长

     3.for (i in 0..100)

    区间[0,100],步长为1

    step:是步长,在for循环中是特有的

    结果如下:

    22、参数泛型

    在kotlin中,对参数类型的泛型要求蛮高的,即使,实参和形参的泛型不一致都会提示错误

    所以在使用过程中,定义参数泛型后,实参和形参一定要匹配,否则很容易报错,这种错误和java中不一样,java只要容器一样,参数是默认类型的。

     有时候在设置泛型一定要格外小心,避免新手排查问题无法定位到

    解决:

    如果你是在不想预先定义好,可以如下:

    1. class FanxingDemo(var map:HashMap <*,*>) {
    2. fun log(){
    3. map.keys;
    4. }
    5. }
    6. fun main() {
    7. var map=HashMap()
    8. var demo=FanxingDemo(map)
    9. }

    形参指定*

    var map:HashMap <*,*>

     实参:

    var map=HashMap()

    或者

    var map: HashMap<*, *> = HashMap()

    或者先定义,后初始化

    var map: HashMap<*, *> 
    
    map= HashMap()

    参数put的时候需要注意:

    key和value要转换成Nothing,否则报错

    map.put("one" as Nothing, 1 as Nothing);
    map.put("two" as Nothing, 1 as Nothing);

    但是这种在编写没有问题,但是在执行的时候会报错

    Nothing是Void类型,所以String不能转Void

    所以在实战中,实参的泛型不要通过*来修饰,Demo如下

    正确:

    var map: HashMap< Any,  Any> = HashMap()
    var key = Any()
    map.put("123",123)

    23、Bean的封装

    在kotlin中,类有两种写法

    1.正常有类体的

    class MyBean() {
        var name: String = ""
        var age: Int = 0
        var map: HashMap<*, *> = HashMap()
    }

    2.无类体,只通过默认构造器完成

    class MyBean(var name: String = "", var age: Int, var map: HashMap<*, *> = HashMap())
    

    3.空类

    class  MyBean()
    

    由于kotlin所有的变量都提供了set()和 get()方法,所以如果做bean来使用,无法处理结果,直接用第二种即可。

    第二种写法也常和Gson、Json配合解析使用

    正常使用如下:

    1. var bean = MyBean("zhangshan", 100, map)
    2. MyLog.log(bean.name + "," + bean.age + "," + bean.map.toString())

    24、String的快捷内置API的使用

    1. val value="123"
    2. value.toFloat()
    3. value.toInt()
    4. value.toDouble()
    5. value.toLong()
    6. value.toCharArray()
    7. value.toList()
    8. value.toBigDecimal()
    9. value.toByte()
    10. value.toShort()
    11. value.toBoolean()

    kotlin的String类型,已提供了好多快捷的转换。我们可以直接使用,这样不用我们自己去换了

    25、异常的抛出方法

    kotlin中有两个异常抛出的方法

    1.在方法体中抛出

    1. try {
    2. Thread.sleep(122)
    3. } catch (e: Exception) {
    4. }

    2.在方法外面抛:通过@Throws跟上异常类

    1. @kotlin.jvm.Throws(Exception::class)
    2. fun testException() {
    3. Thread.sleep(12)
    4. }

    26、Synchronized同步锁使用

    kotlin的同步锁用两种用法

    第一种:变量默认方法加锁

    我们都知道,kotlin的变量默认有set和get方法。所以同步锁在锁变量的时候,可以单独对变量的set和get进行加锁。

    Synchronized

    class TestSyn {
        @set:Synchronized
        @get:Synchronized
        var instance = TestSyn()
    }

    加锁格式:@+(set、get)+ :+Synchronized

    field域不能直接对变量加锁,因为变量不支持,锁只支持方法

    错误写法:

    @Synchronized
    var instance = TestSyn()
    

    第二种:自定义方法加锁

    自定义方法加锁直接在方法名上方插入,如下:

    1. @Synchronized
    2. fun test(){
    3. }

    格式 :@Synchronized

    日常我们了解这两种方式已够开始使用

    27、Volatile:原子一致性

    这些都是属于jvm的,直接@引入即可

    1. @Volatile
    2. var a: Int = 10;

    28、占位符:${value}

    在java中,如果需要在一个String文本中占位一个坑,然后通过数据替换,常见的做法就是MessageFormat来处理。在kotlin中占位可以直接在text文本中使用

    1. var name="zhangshan"
    2. val text="我的名字叫${name}名字长度${name.length}"

    占位符的使用,可以简化我们正常的拼接和formate转换。

    29、可变参数:vararg

    在kotlin中可以参数需要通过vararg来修饰,

    Java :

    String... arry

    kotlin:

    vararg var arry:String

    class Test( vararg var arry:String)

    30、条件执行:let

    let的是kotlin特有的条件,通过自身一些条件,执行let体

    如:

    if(entity!=null){

    initView();

    }

    let:

    1. entity?.let{
    2. initView()
    3. }

    31、for的使用

    遍历:forEach

    java:

    for(object item:arry)

    kotlin:

    arry.forEach {
        it
    }

    遍历:in

    我们正常遍历Java如下:for(int a=0;a<100;a++),kotlin不支持这种写法,kotlin的for搭配in使用

    所以我们正常使用

    for(index in 0..100)

    区间在[0,100],因为数据小标都是size-1,如果我们不小心导致数组越界

    所以在kotlin集合有一个字段已处理了:indices

    indices

    1. public val Collection<*>.indices: IntRange
    2. get() = 0..size - 1
    arry.indices=arr.size-1

    我们在遍历采用indices属性,就可以避免数组越界的问题

  • 相关阅读:
    ACFS文件系统系统重启后权限不能保持的问题
    web安全应用-XSS跨站脚本初级
    计算机毕业设计之java+javaweb的超市库存管理系统
    开始使用AspectJ-实现步骤@Aspect,@Before,还有其中的JoinPoint参数
    华为eNSP配置防火墙策略
    记使用docker部署项目出现问题
    位运算(Bit Operation)
    记录一次工作中的大表优化方案
    Ubuntu 22.04 Docker安装笔记
    LiveData 的使用和原理
  • 原文地址:https://blog.csdn.net/qq36246172/article/details/128067196