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