[AS3.6.1]Kotlin学习笔记3(简化操作,泛型)

    技术2022-07-11  157

    前言

    kotlin学习第三篇文章! 历史文章 [AS3.6.1]Kotlin学习笔记1(基本声明,函数,条件) [AS3.6.1]Kotlin学习笔记2(常量,数组,修饰符)

    简化构造器

    我们还是从kotlin的构造器开始讲解,前面我都说过kotlin中初始化构造器需要加入新的关键字constructor其实在kotlin中我们可以更简单的创建构造器

    //按java写法的kotlin class KClass { var name: String constructor(name: String){ this.name = name } } //简化1 class KClass constructor(name: String){ var name: String init { this.name = name } } //简化2 class KClass constructor(name: String){ var name:String = name } //简化3 class KClass constructor(var name: String){ } //简化4 class KClass(var name: String){ }

    我们可以看到kotlin可以简化到甚至不需要你创建对象和赋值,如果我们需要创建多个构造函数,我们该如何实现呢?比如我们的各种View类,基本上都有三个构造方法,区别如下

    //java public class JView { private Context context; private AttributeSet attrs; private int defStyleAttr; public JView(Context context) { this(context, null); } public JView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public JView(Context context, AttributeSet attrs, int defStyleAttr) { this.context = context; this.attrs = attrs; this.defStyleAttr = defStyleAttr; } } //kotlin class KView(var context: Context, var attrs: AttributeSet?, var defStyleAttr: Int){ constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0){ } constructor(context: Context) : this(context, null){ } } //再次简化 class KView(var context: Context, var attrs: AttributeSet? = null, var defStyleAttr: Int = 0){ }

    可以看到写法上面是简洁了非常的多,甚至可以说是一个构造函数实现了多个构造函数。

    简化函数

    由上面的简化构造器,我们发现可以直接为参数设置默认值实现一个函数实现多个函数的功能。这只是函数的其中一个简化功能,那么还有哪些简化功能呢?下面我们继续展示

    单行函数 = 替代 {} // fun add(a: Int, b: Int): Int{ return a + b } //简化 fun add(a: Int, b: Int): Int = a + b 函数设置默认值

    上面简化构造器的时候我们使用过默认值,实际使用的时候,需要设置命名参数才可以正常使用,比如下例

    //有默认值的函数 fun add3(a: Int, b: Int = 3, c: Int = 5): Int = a + b + c //调用结果 println(add3(1, 2, 3)) // 1+2+3 = 6 println(add3(5, c = 6)) // 5+3+6 = 14

    这边看到我们设置了一个命名参数c = 6 就是告诉函数我第二个参数是修改c的值 b还是按照默认值使用。

    本地函数(嵌套函数)

    可以认为是不暴露给外部查看,看下面简单登录案例

    fun login(user: String, pwd: String){ if (user.isEmpty()) { throw NullPointerException("账号为空") } if (pwd.isEmpty()) { throw NullPointerException("密码为空") } println("$user 登录成功") }

    我们经常会加一些判断代码,如果我们不想暴露就可以加一个嵌套函数来实现

    fun login(user: String, pwd: String){ fun validate(value: String, error: String) { if (value.isEmpty()) { throw NullPointerException(error) } } validate(user, "账号为空") validate(pwd, "密码为空") println("$user 登录成功") }

    这是一个简单嵌套函数,也说明kotlin的函数可以写在各个地方和java的方法比地方多了些。

    简化字符串

    第一篇我们讲到声明变量的时候只是稍微涉及到一点String,在kotlin中String的使用也简化了许多

    简化String.format //java String name = "Java"; Log.i(name , String.format("你好,%s", name)); //kotlin val name = "Kotlin" println("你好,$name")

    kotlin中的简化实际是${}这边因为是直接打印就省略了{} 实际使用的时候是可以在{}中进行各种简单的算法判断等的,这个简化也顺便简化了getString方法

    <string name="test" formatted="true">测试 %s</string> //java Log.i("name", getString(R.string.test, "Java")); //kotlin println(getString(R.string.test, "Kotlin")) 简化转译字符

    在java中我们经常需要写转译字符类似\\ \"这些,在kotlin中引入了"""原生字符串来实现

    //java String name = "Java"; Log.i(name , "hello world!"); Log.i(name , String.format(" 我是 %s", name)); Log.i(name , " !!!\\n"); //kotlin val name = "Kotlin" val str = """hello world! 我是 $name !!!\n""" println(str)

    打印结果如下

    I/Java: hello world! I/Java: 我是 Java I/Java: !!!\n I/System.out: 你好,KotlinC.kt I/System.out: hello world! I/System.out: 我是 张三 I/System.out: !!!\n

    这边我们看到

    回车换行代替了一次打印方法$还是可以直接使用kotlin中的"\n"不等于换行

    这边还有一点就是使用"""之后换行之后只要有回车就需要删除前面的全部空格,感觉格式非常奇怪,其实是可以使用trimMargin()方法直接删除前面的空格的,默认截取字段是|

    //修改 val name = "Kotlin" val str = """hello world! | 我是 $name | !!!\n""".trimMargin() println(str)

    注:如果想打印$写法是${'$'}

    操作符简化

    数组集合操作简化

    在java中我们要处理一些数组集合相对比较麻烦,要么使用Collections对象进行操作,要么自己遍历操作,后来RxJava的链式模式里面有了一些方法如filter、map等,在kotlin中也有这些方法

    val intArray = intArrayOf(1, 2, 3, 4, 5) val strList = listOf("aa", "b", "cc", "dd", "f") intArray.forEach { print("$it ") //1 2 3 4 5 } strList.forEach { str -> print("$str ") //aa b cc dd f } val newStrList = strList.filter { it.length > 1 }.toList() println(newStrList) // aa cc dd val newIntArray = intArray.map { it + 1 }.toList() println(newIntArray) //2 3 4 5 6 intArray.reverse() for (i in intArray) { print("$i ") //5 4 3 2 1 }

    这边写了几个常用的函数,实际使用中还有很多。

    三元运算符简化

    我们再说下?.和?:,?.我们知道是kotlin的空安全判断,那么?:是什么呢?我们可以理解为java的? :(三元运算符)

    //java String name; int len = name == null ? 0 : name.lenght //kotlin var name: String? = null val len: Int = name?.length ?: 0 判断简化

    在java中我们经常用equals来判断字符串之间是否相等和==来判断数字之间是否相等,在kotlin中==直接替换了equals和==,kotlin新增===来判断引用的内存地址是否相等,那么这两个有什么区别,案例如下

    val a = "a" val b = "b" val c = "a" println("a == b ${a == b}") //false println("a === b ${a === b}") //false println("a == c ${a == c}") //true println("a === c ${a === c}") //true val d = a val e = c println("d === e ${d === e}") //true

    我们可以看到==就是直接判断两个值是否相等,而===要类型一样的前提是值也要相等。

    泛型简化

    讲到泛型我们要先说下java中的泛型,可能我们最经常使用的泛型是List<T extends View>这种类似的写法,但在java中我们使用泛型的时候经常有? extends 和 ? super这两个关键字

    ? extends

    虽然我们经常会写T extends实际当你不需要设置通配符的时候是可以直接? extends的。比如

    List<? extends View> views = new ArrayList<>(); //自身 List<? extends View> textViews = new ArrayList<TextView>(); //直接子类 List<? extends View> buttons = new ArrayList<Button>(); //间接子类

    这样当你需要使用一个父类参数的时候就可以直接使用泛型来扩大方法的使用范围比如

    private void logId(List<? extends View> views){ for (View view : views) { Log.i("name", "viewId = " + view.getId()); } } logId(views); logId(textViews); logId(buttons);

    这样这个方法让三个list都可以使用。只能读取值!

    ? super

    其实? super就是和? extends 刚好相反的概念。? extends 是子类扩展,而? super是父类扩展,案例如下

    List<? super Button> buttons = new ArrayList<>(); //自身 List<? super Button> textViews = new ArrayList<TextView>(); //直接父类 List<? super Button> views = new ArrayList<View>(); //间接父类

    这父类扩展有什么用呢?比如一下情况,我们想在一个list之后统一加入一个尾巴按钮。

    private void addTailButton(List<? super Button> buttons){ Button button = new Button(ctx); buttons.add(button); } addTailButton(buttons); addTailButton(textViews); addTailButton(views);

    我们使用? super就可以让快速实现,不需要设置成一个List<View>来改造方法了。

    小结

    我们发现使用了? extends 的list只能用来获取值而不能设置值,使用了? super的刚好相反,只能添加值而不能用来获取值。这被称为 PECS 法则:「Producer-Extends, Consumer-Super」

    kotlin的泛型

    经过上面的说明,我们来看下kotlin中泛型关键字in 和 out,他们和java的区别如下

    //java List<? extends View> textViews = new ArrayList<TextView>(); List<? super Button> textViews = new ArrayList<TextView>(); //kotlin val textViews: List<out View> = ArrayList<TextView>() val textViews: List<in Button> = ArrayList<TextView>()

    在kotlin中就很明显的告诉了你 PECS 法则,out就在单词上告诉你我是输出的(只读不能写),in就是输入的(只写不能读)。

    我们在java中有时候会经常用到List<?>这样的写法代表Object数组因为List<?>是List<? extends Object>的简写,在kotlin中我们知道Any代替了Object,那么在kotlin中的写法就是List<* out Any>,我们可以看到kotlin中*代替了?,案例如下

    //java List<? extends Object> list = new ArrayList<>(); List<?> list = new ArrayList<>(); //kotlin val list : List<* out Any> = ArrayList<>() val list : List<*> = ArrayList<>()

    where

    在java中如果我们要让一个泛型必须是多个类的子类是使用&连接,在kotlin中用的是关键词where,代码如下

    //java public class A<T extends View & Activity> //kotlin class A<T> where T : View, T : Activity

    reified

    在java中我们判断一个对象是否属于某个泛型会使用isInstance方法,在kotlin中使用的是关键词reified和inline,代码如下

    //java public <T> void check(Object item, Class<T> type) { if (type.isInstance(item)) { //.... } } //kotlin inline fun <reified T> check(item: Any) { if (item is T) { //... } }

    总结

    本篇基本是kotlin对于java的简化操作,和语法糖的使用说明!

    资料

    Kotlin 里那些「更方便的」 Kotlin 的泛型

    Processed: 0.011, SQL: 9