美文网首页
Kotlin的解析(中)

Kotlin的解析(中)

作者: 雾里看花六月天 | 来源:发表于2018-12-02 22:47 被阅读8次

    前言

      通过上一篇的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以及其他类扩充方法,比较灵活方便,也希望此篇幅的知识对你有稍许的帮助

    相关文章

      网友评论

          本文标题:Kotlin的解析(中)

          本文链接:https://www.haomeiwen.com/subject/khwufqtx.html