Kotlin语法总结:Java代码文件转Kotlin代码文件改造注意细节

Posted LQS_Android

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin语法总结:Java代码文件转Kotlin代码文件改造注意细节相关的知识,希望对你有一定的参考价值。

我们可以通过androidStudio编辑器将Android的Java语言代码自动转化为Kotlin语言代码,但是这种转换是机械性的转换,没有体现出Kotlin语言简洁、高效的精髓,因此需要我们懂得Kotlin语法,对齐进行修改,已达到最优的高效代码:

下面将举几个简单的例子进行说明:

构造函数改造:

下面一个简单的数据Bean它有三个属性字段和2个构造函数,其中一个无参数的构造函数,另一个是多参数的构造函数,下面利用转化工具进行转化,结果如下:

package com.example.app.entity;

/**
 * Copyright (c)2021 网络科技有限公司
 *
 * @author: LQS
 * @date: 2021/8/16
 * @description:
 */
class UserInfo {
    private String name;
    private String password;
    private String code;

    public UserInfo() {
    }

    public UserInfo(String name, String password, String code) {
        this.name = name;
        this.password = password;
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }
}

转化为Kotlin代码后,结果为:

/**
 * Copyright (c)2021 网络科技有限公司
 *
 * @author: LQS
 * @date: 2021/8/16
 * @description:
 */

 class UserInfo {
    var name: String? = null
    var password: String? = null
    var code: String? = null

    constructor() {}
    constructor(name: String?, password: String?, code: String?) {
        this.name = name
        this.password = password
        this.code = code
    }
}

修改后的代码:

/**
 * Copyright (c)2021 网络科技有限公司
 *
 * @author: LQS
 * @date: 2021/8/16
 * @description:
 * ① 注意这里以多参数构造器为主构造器,构造器代码写到类名后面
 * ② 以构造属性var声明三个字段,Kotlin会自动为三个属性字段name、password:、code生成getter()、setter()方法;
 * ③ 单构造器放在类中,需要调用主构造器,参数不知道就传null;
 */

class UserInfo constructor(var name: String?, var password: String?, var code: String?) {
    //注释见上
    constructor():this(null,null,null) {}
}

 数据类:关键字data

现实开发中的通常使用Model或者Bean作为数据的容器,它不仅需要实现拥有一些字段用于保存数据外,还需要实现一些常见的equals()、hashCode()、toString()、...等用于数据操作的方法。对于这样的数据容器类,在Kotlin语言中被统一定义为数据类,用在class前面加关键字data标识。

添加data关键字后,事实上它的底层重写 Any的三个函数equals()、hashCode()、toString() ,并增
加了一个 copy 函数。对于 equals 函数就是比较所有属性全部相等, equals 才返回true。对 toString 函数是将所有属性连接拼接成一个字符串。
提示   使用 data 声明的数据类的主构造函数中参数一定声明为 val var 的,不能省略。而普通类可以省略的,例如class User(name: String, password: String)代码是可以编译通过的。但是 data class User(name: String, password: String)的代码是不能编译通过的。
/**
* 添加关键字 data
**/
data class UserInfo constructor(var name: String?, var password: String?, var code: String?) {
    //注释见上
    constructor():this(null,null,null) {}
}
**
 * Copyright (c)2021 网络科技有限公司
 * @author:  LQS
 * @date: 2021/8/16
 * @description:
 */
class Test {
    private val user = UserInfo("AA","BB","CC");

    /**
     *  UserInfo作为数据类型,默认自动实现copy()函数
     */
    private val userCopy= user.copy();
    fun main(){
        println(user);
        println(userCopy)
        //在Kotlin当中运算符== 表示调用equals()函数
        println(user==userCopy)
        //在Kotlin当中运算符=== 表示地址比较,是否是同一个对象
        println(user===userCopy)
    }

}
解构数据类 :
数据对象是一个数据容器,可以理解为多个相关数据被打包到一个对象中。解构进行相反
的操作,是将数据对象拆开将内部的属性取出,赋值给不同的变量。解构不仅仅适用于数
据对象,也适用于集合对象。

 在Java中需要使用【对象.字段属性】的方式获取值,而在Kotlin中,可以通过解构获取值:

 解构形式:

  再比如:


 fun main(args: Array<String>) {
   //创建User对象 
   val user1 = User("Tony", "123")  
   //解构
   val(name1,pwd1) = user1  
   println(name1) //Tony 
   println(pwd1) //123 


  //省略解构password 
   val(name2, _) = user1 
   println(name2) //Tony 
}
使用Elvis运算符(?:)

通过Elvis运算符进行优化: 

 

when分支结构和表达式 

when分支结构(when语句): 

when (表达式) {
   分支条件表达式1 -> {
         语句组1
   }
    分支条件表达式2 -> {
         语句组2
    }
   ...
    分支条件表达式n -> {
         语句组n
    }
    else -> {
         语句组n+1
    }
}

when结构当做语句时,最后的else分支可以省略。另外,如果语句组所在的代码块只有一条语句,可以省略大括号。

when表达式
when 语句示例代码显然还不够简洁,而when表达式也可以使得代码变得更加简洁。when表达式语法结构如下:
 
val(或var) bar = when (表达式) {
      分支条件表达式1 -> {
            语句组1
            表达式
      }
      分支条件表达式2 -> {
            语句组2
            表达式
      }
      ...
      分支条件表达式n -> {
            语句组n
            表达式
      }
      else -> {
            语句组n+1
            表达式
      }
}

when表达式每一个分支最好是一条表达式,最后结束将表达式计算结果赋值给变量bar。需要注意的是when表达式不能省略else分支。

  /**
     * 静态内部类  internal修饰
     */
    class LessonViewHolder internal constructor(itemView: View) : BaseViewHolder(itemView) {
        internal fun onBind(lesson: Lesson) {
            var date = lesson.date ?: "日期待定" //默认值
            setText(R.id.tv_date, date)
            setText(R.id.tv_content, lesson.content)
            lesson.state?.let {
                setText(R.id.tv_state, it.stateName())
                /**
                 * when分支结构、表达式 是有返回值的
                 */
                val colorRes = when (it) {
                    Lesson.State.PLAYBACK -> R.color.playback
                    Lesson.State.LIVE -> R.color.live
                    Lesson.State.WAIT -> R.color.wait
                }
                val backgroundColor = itemView.context.getColor(colorRes)
                getView<View>(R.id.tv_state)!!.setBackgroundColor(backgroundColor)
            }
        }

for循环语句

    fun showPlayback() {
        val playbackLessons: MutableList<Lesson> = ArrayList() //List初始化 不能修改
        for (lesson in lessons) {
            if (lesson.state=== Lesson.State.PLAYBACK) {
                playbackLessons.add(lesson)
            }
        }
        activity!!.showResult(playbackLessons)
    }

使用forEach()函数、lambda表达式规则对上面的for()循环进行优化,先来看一些知识点补充:

关于forEach()函数补充:

forEach函数适用于CollectionMap集合,以及数组,函数只有一个函数类型的参数,实参往往使用尾随形式的Lambda表达式。在执行时forEach会把集合或数组中的每一个元素传递给Lambda表达式(或其他的函数引用)以便去执行:

        
   public inline fun <T> Iterable<T>.forEach(action: (T) → Unit): Unit
         
fun main(args: Array<String>) {

       val strArray = arrayOf("张三", "李四", "王五", "董六") //创建字符串数组
       val set = setOf(1, 3, 34, 54, 75) //创建Set集合
       val map = mapOf(102 to "张三", 105 to "李四", 109 to "王五") //创建Map集合

       println("-----遍历数组-----")
       strArray.forEach {
              println(it)
       }

       println("-----遍历Set集合-----")
       set.forEach {
              println(it)
       }
       
       println("-----遍历Map集合k,v-----")
       map.forEach { k, v -> ①
              println("$k - $v")
       }
       
       println("-----遍历Map集合Entry-----")
       map.forEach { ②       
              println("${it.key} - ${it.value}")
      }
}

输出结果:

-----遍历数组集合-----
"张三"
"李四"
"王五"
"董六"
-----遍历Set集合-----
1
3
34
54
75
-----遍历Map集合k,v-----
102 - 张三
105 - 李四
109 - 王五
-----遍历Map集合Entry-----
102 - 张三
105 - 李四
109 - 王五

lambda表达式的使用补充:

     { 参数列表 ->
             Lambda体
     }

其中,Lambda表达式的参数列表与函数的参数列表形式类似,但是Lambda表达式参数列表前后没有小括号。箭头符号将参数列表与Lambda体分隔开,Lambda表达式不需要声明返回类型。Lambda表达式可以有返回值,如果没有return语句Lambda体的最后一个表达式就是Lambda表达式的返回值,如果有return语句返回值是return语句后面的表达式。

提示 Lambda 表达式与函数、匿名函数一样都有函数类型,但从 Lambda 表达式的定义中只能看到参数类型,看不到返回类型声明,那是因为返回类型可以通过上下文推导出来。
简化规则:
参数类型推导简化:
       { a: Int, b: Int -> a + b }  简化为:{ a, b -> a + b }
②尾随lambda表达式规则:
使用尾随 Lambda 表达式,Lambda表达式可以作为函数的参数传递,如果 Lambda 表达式很长,就会影响程序的可读性。如果一个函数的最后一个参数是Lambda 表达式,那么这个 Lambda 表达式可以放在函数括号之后。
③省略参数声明规则:
如果 Lambda 表达式的参数只有一个,并且能够根据上下文环境推导出它的数据类型,那么这个参数声明可以省略,在Lambda 体中使用隐式参数 it 替代 Lambda 表达式的参数。
优化示例代码如下:
        /**
         * public inline fun <T> Iterable<T>.forEach(
         *   action: (T) → Unit
         *  ): Unit
         */
        lessons.forEach({lesson->
            if (lesson.state=== Lesson.State.PLAYBACK) {
                playbackLessons.add(lesson)
            }
        })

 根据②尾随lambda表达式规则可知,如上面代码所示的lambda表达式可以作为forEach(){}函数的参数传递,也可以写成尾随的形式,放到花括号{ }中:

        lessons.forEach(){lesson->
            if (lesson.state=== Lesson.State.PLAYBACK) {
                playbackLessons.add(lesson)
            }
        }

 根据③省略参数声明规则,如果Lambda表达式的参数只有一个,并且能够根据上下文环境推导出它的数据类型,那么这个参数声明可以省略,在Lambda体中使用隐式参数it替代Lambda表达式的参数,并且函数名后面的()可以省略:

       lessons.forEach{it->
            if (it.state=== Lesson.State.PLAYBACK) {
                playbackLessons.add(it)
            }
        }

这时编译器会提示我们,如果lambda表达式的参数列表参数可用it代替,it可以省略,最后简化结果如下:

       lessons.forEach {
            if (it.state=== Lesson.State.PLAYBACK) {
                playbackLessons.add(it)
            }
        }
filter函数
public inline fun <T> kotlin.collections.Iterable<T>.filter(predicate: (T) -> kotlin.Boolean): kotlin.collections.List<T> {

    /* compiled code */ 

}
过滤操作使用 filter 函数,它可以对 Collection 集合、 Map 集合或数组元素进行过滤,Collection集合和数组返回的是一个 List 集合, Map 集合返回的还是一个 Map 集合。

data class User(val name: String, var password: String) 
    val users = listOf( 

        User("Tony", "12%^3"),
        User("Tom", "23##4"),
        User("Ben", "1332%#4"),
        User("Alex", "ac133")
    )
调用:
fun main(args: Array<String>) {
  users.filter { it.name.startsWith("t", ignoreCase = true) }.forEach {println(it.password) } 
}

输出结果:

12%^3
23##4

表达式it.name.startsWith("t", ignoreCase = true)是判断集合元素的name属性是否是t字母开头的,ignoreCase = true忽略大小写比较。filter函数处理完成之后的数据,由原来的四条数据编程了现在的两条数据。

 forEach函数用来遍历集合元素,它的参数也是一个Lambda表达式,所以forEach {println(it.password) }是将集合元素的password属性打印输出。

map函数

public inline fun <T, R> kotlin.collections.Iterable<T>.map(transform: (T) -> R): kotlin.collections.List<R> { /* compiled code */ }

public inline fun <T, R> kotlin.collections.Iterable<T>.mapIndexed(transform: (kotlin.Int, T) -> R): kotlin.collections.List<R> { /* compiled code */ }

映射操作使用map函数,它可以对Collection集合、Map集合或数组元素进行变换返回一个List集合。

fun main(args: Array<String>) {
  users.filter { it.name.startsWith("t", ignoreCase = true) } .map { it.name } .forEach{ println(it)} 
}
 
上述代码使用 filter 函数和 map 函数对集合进行操作,先 使用filter 函数过滤,只有两元素,元素类型是 User 对象。再使用 map 函数对集合进行变换,it.name 是变换表达式,将计算的结果放到一个新的 List集合中,新的集合元素变成了字符串,这就是 map 函数变换的结果。如下图:

聚合函数-归纳函数

聚合操作会将Collection集合或数组中数据聚合起来输出单个数据,聚合操作中最基础的是归纳函数reducereduce函数会将集合或数组的元素按照指定的算法积累叠加起来,最后输出一个数据。

data class Song(val title: String, val durationInSeconds: Int) 
    val songs= listOf(Song("Speak to Me", 90), 
                      Song("Breathe", 163),
                      Song("On he Run", 216),
                      Song("Time", 421),
                      Song("The Great Gig in the Sky", 276),
                      Song("Money", 382),
                      Song("Us and Them", 462),
                      Song("Any Color You Like", 205),
                      Song("Brain Damage", 228),
                      Song("Eclipse", 123)
    )

调用:

fun main(args: Array<String>) {

   //计算所有歌曲播放时长之和
   val durations = songs.map { it.durationInSeconds } .reduce { acc, i -> acc + i }
   println(durations) //输出:2566
}

首先声明了一个数据类Song它是保存Song对象的List集合。 调用map函数变换songs集合数据,返回歌曲时长(durationInSeconds)的List集合,再调用reduce函数计算时长,其中acc参数是上次累积计算结果,i当前元素,acc + i表达式是进行累加,这里表达式是关键,根据自己需要这表达式是不同的。

Kotlin语法糖规则、运算符、函数操作有很多,总结到此,起到抛砖引玉作用。

欢迎评论、关注!后续继续总结!

以上是关于Kotlin语法总结:Java代码文件转Kotlin代码文件改造注意细节的主要内容,如果未能解决你的问题,请参考以下文章

代码行数减少30-90%!多邻国从Java迁移到Kotlin的奇妙体验

Kotlin:使内部函数对单元测试可见

Android为什么要从Java改用Kotlin

Android为什么要从Java改用Kotlin

Android工程java项目如何转kotlin

Kotlin总结