Kotlin的解析(中)

1,383

前言

  通过上一篇的Kotlin介绍了一些基本的变量,方法,函数,类等的基本讲解,接下来,让我们更近一步的学习,深入一点的知识

1.  枚举类和扩展

1.1 枚举类

  Kotlin的枚举类和Java的非常相似,具有类的特性,一般会将可枚举的同类型一组值作为枚举类定义,由于每一枚举都是个对象,可能在性能上还是不推荐用,Android中已经用注解慢慢取代了,这种枚举的使用

1.1.1 枚举类的基本用法

  在kotlin中,枚举类型是以类的形式存在的,因此成为枚举类

enum class Direction {
NORTH,SOUTH,WEST,EAST
}

1.1.2 枚举值指定对应的数值

  从下面的代码可以看出,除了基本的语法不同,实现的规则和Java的非常相似

enum class Direction private constructor(val d:Int){ 
    SOUTH(1),WEST(2);

    override fun toString(): String {
        return d.toString()
    }
}

fun main(args:Array<String>){
var dir1 : Direction = Direction. SOUTH
var dir2  = Direction. WEST
println(dir1)//输出的是1
println(dir2)//输出的是2
}

1.1.3 枚举的其他拓展

  var dir1 : Direction = Direction. WEST
        Log.i("tag",dir1.name)//输出的是:WEST
        Log.i("tag",dir1.ordinal.toString()) //输出的是在枚举中的位置 1
        Log.i("tag",dir1.toString()) //输出的是传入的数值
        Log.i("tag",Direction.valueOf("WEST").toString()) //输出的是传入的数值

//如果要得到所有枚举的值,可以使用values的方法
for(d in Direction.values()){
println(d)
}
1.2 扩展

  扩展是Kotlin中非常重要的功能,可以在没有源代码的情况下向类中添加成员,也可以啊子团队开发的情况下,通过扩展,将模块分散给多个人开发

1.2.1 扩展原生API

Kotlin的原生的集合扩展

//这个方法放哪里呢?一般都放在Kotlin文件顶层,当然,也可以放在调用swap方法的位置前面
fun MutableList<Int>.swap(index1:Int ,index2:Int){
            //为MutableList添加一个swap的方法,用于交互任意两个集合元素的位置
            var tmp = this[index1]
            this[index1] = this[index2]
            this[index2] = tmp;
        }

val tab = mutableListOf(1,2,3)
        tab.swap(0,2) //原生里面是没有这个方法的,通过扩展就可以了,牛逼
        Log.i("tag","jihe:  " + tab.toString())//输出[3,2,1]

JDK标准的集合类ArrayList添加了一个hellow的方法

 fun ArrayList<Int>.hellow(string: String){
            println(string)
        }

        var list: ArrayList<Int> = ArrayList();
        list.add(20)
        list.add(30)
        list.add(40)
        list.add(50)
        list.swap(0,2)//这个是原生自带的
        list.hellow("牛逼吧!!!嘻嘻") //这个是上面自己写的一个方法

1.2.2 扩展自定义类

  扩展类的目的很多,除了系统类需要扩展之外,我们自己编写的类有时候也需要扩展,但是我们有不想去类里面修改,这时候这个功能就相得益彰

open class Parent(val va1: Int, val va2: Int) {//使用open声明,才能允许其他类继承
    var mVal1 = va1
    var mVal2 = va2
    fun add() = this.mVal1 + this.mVal2
}

class Child(va1: Int, va2: Int) : Parent(va1, va2) {
    fun sub() = mVal1 - mVal2
}


        fun Parent.log() {
            Log.i("tag", "父类:" + "${mVal1} +${mVal2} = ${add()}")
        }

        fun Child.log() {
            Log.i("tag", "子类:" + "${mVal1} -${mVal2} = ${sub()}")
        }

        var par1: Parent = Parent(1, 2)
        var par2: Parent = Child(1, 2)
        var chil1: Child = Child(1, 2)

        par1.log()//父类:1 +2 = 3
        par2.log()//父类:1 +2 = 3
        chil1.log()//子类:1 -2 = -1



open class Parent(val va1: Int, val va2: Int) {
    var mVal1 = va1
    var mVal2 = va2
    fun add() = this.mVal1 + this.mVal2
//内部成员函数,和扩展同名,扩展覆盖不了内部
fun log() {
        Log.i("tag", "父类:自己" + "${mVal1} +${mVal2} = ${add()}")
    }
}


fun Parent.log() {
            Log.i("tag", "父类:" + "${mVal1} +${mVal2} = ${add()}")
        }

        fun Child.log() {
            Log.i("tag", "子类:" + "${mVal1} -${mVal2} = ${sub()}")
        }

        var par1: Parent = Parent(1, 2)
        var par2: Parent = Child(1, 2)
        var chil1: Child = Child(1, 2)

        par1.log()//父类:自己1 +2 = 3
        par2.log()//父类:自己1 +2 = 3
        chil1.log()//父类:自己1 +2 = 3
 

  上面可以看出:(1)尽管par2的实例对象是Child,但是通过扩展的方法,并没有重写父类的扩展方法,因此par2调用的还是父类的方法。 (2)类内部成员函数和通过扩展添加的成员函数冲突,那么内部成员函数的优先级更高,通过扩展无法覆盖内部成员函数

1.2.3 扩展伴随对象

  如果类中有伴随对象(由于Kotlin类不支持静态成员变量,因此引入了伴随对象,来解决类没有静态成员所带来的尴尬),那么可以利用扩展对象添加成员

class SubClass  {
    companion object {
    }
}

fun  SubClass.Companion.nihao(){
           Log.i("tag","hello word!")
        }

SubClass.nihao();//不需要实例,直接类名调用
//扩展范围,放大
//在类中也可以使用扩展

2.  数据类和封装

  数据类和封装是Kotlin中两种特殊的类,前者用于描述数据和相应的操作,后者相当于枚举类的扩展,用于描述有限的数据 #####2.1 数据类   数据类是Kotlin 的一个语法糖,Kotlin编译器会自动为数据类生产一些成员函数,以提高开发效率

2.1.1 使用数据类

//一般的类的书写
class User(var name: String, var sex: Int)  {
    var mName = name
    var mSex = sex
    override fun equals(other: Any?): Boolean {
    //重写,是不是感觉的很不爽,要写这么多
        if (other is User) {
            if (mName == other.mName && mSex == other.mSex) {
                return true
            }
        } else {
            return false
        }
        return false
    }
    override fun toString(): String {
      //重写,是不是Java中很烦,yes,很烦
        return "User {name = $mName \n sex = $mSex }"
    }
}

  从上面User可以看出,只有name和sex是必要的,其余的都可以自动的推倒,而怎么弄呢?其实Kotlin中提供了,那就是在class前面加上data关键字就行了

data class Student(var name: String) {
    constructor():this("sdfdf")//为了添加一个无参的构造函数
}

var student = Student("xixi")
var student1 = Student("haha")
Log.i("tag", student.toString()); //输出:Student(name=xixi)
Log.i("tag", student1.toString());//输出: Student(name=haha)
Log.i("tag", student.equals(student1).toString());//输出:false

  数据类和普通的类最大的不同,就是数据类可以根据构造器的参数自动生成相关的代码;如果Kotlin中,同事具有普通类,以及存储和管理数据的功能,建议直接使用数据类

编写数据类注意事项

(1)主构造器至少有一参数

(2)主构造器的参数必须标记为var/val

(3)数据类不能是抽象类,open类,封闭类(sealed)类或内部类(inner) 由于主构造器必须要有一个参数,不可能存在没有参数的主构造器,要想拥有,两种方案解决:

(1)为主构造器每个参数都加上默认值

data class User(val name :String="Bill", var age :Int = 10)

(2)添加一个没有参数的构造器,调用主构造器时,指定默认参数

data class User(var name : String ,var age :Int){
//次构造函数
constructor():this("Devin","18")
}

2.1.2 数据类成员的解构

  数据类成员解构,这里关键解构,也就是解除结构,在数据类中,用属性表示数据,这些属性属于同一数据类,要想使用这些属性,必须首先引用数据对象,这里的解构就是指将这些数据对象的属性提取出来,分别单独赋值给变量

  var student = Student("大家好")
  val (name) = student
  Log.i("tag", ";;;;;;"+name );//;;;;;;大家好

2.1.3 封闭类

  封闭类也是Kotlin的一个语法糖,可以把它理解为枚举的扩展,一个封闭类,前面用sealed,可以有任意多个字对象,封闭类的值只能是这些字对象,使用封闭类的好处,主要是与when表达式配合,不需要再使用else形式

sealed class Icon ()
class Small() : Icon()
class Big() : Icon()

fun eval(icon: Icon) {
        when (icon) {
            is Small -> {
            }
            is Big -> {
            }
        }
    }

var big = Big()
        eval(big)

3.  泛型

3.1 泛型基础

  所谓的泛型,就是指在定义数据结构时,只指定类型的占位符,等到使用该数据结构时在指定具体的数据类型

class Box<T>(t :T){
    var value = t
}

var box = Box<Int>(1)
Log.i("tag", ";;;;;;" + box.value);
3.2 类型变异

  Kotlin泛型并没有提供通配符,取而代之的是out和in的关键字(1)用out声明的泛型占位符只能用在获取泛型类型值的地方(2)用in声明的泛型只能在设置泛型类型值的地方

(1). 使用out关键字

abstract class Source< out T> {
   abstract fun NextT(): T
}

fun demo(strs: Source<String>) {
//编译通过,因为T是一个out类型参数
    val ni: Source<Any> = strs
    
}

(1).使用in关键字

abstract class Comparable<in T>{
    abstract fun comparaTo(other:T)
}

fun demo (x:Comparable<Number>){
//1.0时Double类型,Double时Number的子类型
    x.comparaTo(1.0)
    val y: Comparable<Double> = x
}
3.3 泛型函数
 fun <T> single(item :T) :T{
        return item
    }

var single = single(1)
 Log.i("tag", ";;;;;;" + single);
3.4 泛型约束

  最常见的约束是上界(upper bound),与Java的extends关键字相同,如果没有指定,默认使用的上界类型Any?,在定义泛型参数的尖括号内,只允许定义唯一一个上界,要是多个就的使用where

fun <T :Parent> convert(item :T){
}

fun<T>clone (list:List<T>,there:T):List<T> where T :Comparable,T:Cloneable{
//.....
}

总结

  通过本章节的学习,了解到了枚举类,数据类,封闭类,泛型,而且学到了非常方便的一个扩展的实用语法,可以很方便的为原生Api以及其他类扩充方法,比较灵活方便,也希望此篇幅的知识对你有稍许的帮助