Kotlin的解析(中)

栏目: IOS · 发布时间: 7年前

内容简介:通过上一篇的Kotlin介绍了一些基本的变量,方法,函数,类等的基本讲解,接下来,让我们更近一步的学习,深入一点的知识Kotlin的枚举类和Java的非常相似,具有类的特性,一般会将可枚举的同类型一组值作为枚举类定义,由于每一枚举都是个对象,可能在性能上还是不推荐用,Android中已经用注解慢慢取代了,这种枚举的使用在kotlin中,枚举类型是以类的形式存在的,因此成为枚举类

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


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Music Recommendation and Discovery

Music Recommendation and Discovery

Òscar Celma / Springer / 2010-9-7 / USD 49.95

With so much more music available these days, traditional ways of finding music have diminished. Today radio shows are often programmed by large corporations that create playlists drawn from a limited......一起来看看 《Music Recommendation and Discovery》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

MD5 加密
MD5 加密

MD5 加密工具