Android Kotlin(1)之《类》

大家好!这是我第三篇文章,之前说Toast例子先欠着哈,等我发完Kotlin系列后继续更新,最后有我的源码,欢迎关注和下载,源码也会逐渐完善。这也是我学习Kotlin后写的第一篇文章,在网上查找很多资料和自己也实验很多,后续我会陆续发布相应Kotlin其他文章,欢迎大家多多支持,我就不介绍相关环境搭建了,这个环境搭建还是很简单的,Android studio有插件支持的,只需要配置好Gradle就好,我就直接正对Kotlin进入使用介绍,第一个要介绍的就是《类》。当然我写的也许不全面,你也可以提出遗漏的,我们大家一起补充完善。

类和继承

1、类(Class)

java类文件:XXX.java
Kotlin类文件:XXX.kt

Kotlin 中使用关键字 class 声明类

class ClassGoKotlin {
}

类声明由类名、类头(指定其类型参数、主构造函数等)和由大括号包围的类体构成。类头和类体都是可选的; 如果一个类没有类体,可以省略花括号。

class ClassGoKotlin

与java区别:都是Class申明,只是java默认是有public,当然java也可以省去public,但是java不可以像Kotlin去掉类的“{}”大括号,主要区别在于构造函数和初始化,如下:

构造函数

在 Kotlin 中的一个类可以有一个主构造函数和一个或多个次构造函数。主构造函数是类头的一部分:它跟在类名(和可选的类型参数)后。

class ClassGoKotlin constructor(param1 : String){
}

如果主构造函数没有任何注解或者可见性修饰符,可以省略这个 constructor 关键字。

class ClassGoKotlin(param1 : String) {
}

主构造函数特性:
1、主构造函数不能包含任何的代码,初始化在init关键字里作为前缀的初始化模块里,例如:

class ClassGoKotlin(param0 : String) {
    var param1 : String? = param0.toUpperCase()
    init {
        //主构造函数初始化
        this.param1 = param0.toUpperCase()
    }

2、主构造函数可以声明属性以及初始化属性,在主构造函数参数前家var或者val就是声明属性,在参数后面可以附上默认的初始值,例如:

class ClassGoKotlin(var param0 : String, var param00 : String = "赋默认初始值") {
    var param1 : String? = param0.toUpperCase()
    init {
        //主构造函数初始化
        this.param1 = param0.toUpperCase()
    }

特别说明:赋有默认值的参数,在调用该类的时候可以不填写该参数,不填写该参数,Kotlin会自动按照默认参数执行
3、如果构造函数有注解或可见性修饰符,这个 constructor 关键字是必需的,并且这些修饰符在它前面,如果没有注解或者可见性修饰符,主构造函数constructor 可以省略,例如:

class Customer public @Inject ClassGoKotlin(name: String) { …… }

4、唯一性,一个类里只有一个主构造函数,
次构造函数特性:
1、次构造函数必须有constructor前缀声明
2、如果类有一个主构造函数,每个次构造函数需要委托给主构造函数。委托到同一个类的另一个构造函数用 this 关键字即可,例如:

class ClassGoKotlin(param0 : String,var param00 : String = "赋默认初始值") {

    var param1 : String? = param0.toUpperCase()

    init {
        //主构造函数初始化
        this.param1 = param0.toUpperCase()
    }
    //每个次构造函数需要委托给主构造函数
    constructor(param1: String,classGoKotlin: ClassGoKotlin) : this(param1){
        this.param1 = param1.toUpperCase()
    }
}

3、可以直接委托或者通过别的次构造函数间接委托,例如:

//每个次构造函数需要委托给主构造函数
    constructor(param1: String,classGoKotlin: ClassGoKotlin) : this(param1){
        this.param1 = param1.toUpperCase()
    }
    //每个次构造函数也可以委托给其他次造函数
    constructor(param1: String, param2 : String , classGoKotlin: ClassGoKotlin) : this(param1,classGoKotlin) {
        this.param2 = param2
    }

特别说明:如果一个非抽象类没有声明任何(主或次)构造函数,它会有一个生成的不带参数的主构造函数。如果你不想你的类有一个公有的构造函数,你可以声明一个私有的主函数

class ClassGoKotlin private constructor () {
}

与java区别:java是不允许在Class这行里添加参数,java类默认是default(默认访问模式,只允许在同一个包中进行访问),Kotlin默认是public(公有的);java里不存在主次构造函数,但是委托还是有的,也是this实现的在代码里实现,例如:

//java
public class ClassGoJava{

    public ClassGoJava(){

    }
    public ClassGoJava(String param1){
        this();
    }

    public ClassGoJava(String param1, String param2){

        this(param1);
    }
}

创建类实例

直接上例子:

class ClassGoKotlin(param0 : String,var param00 : String = "赋默认初始值") {

    var param1 : String? = param0.toUpperCase()
    var param2 : String? = null
    var param3 : String? = null
    init {
        //主构造函数初始化
        this.param1 = param0.toUpperCase()
    }
    //每个次构造函数需要委托给主构造函数
    constructor(param1: String,classGoKotlin: ClassGoKotlin) : this(param1){
        this.param1 = param1.toUpperCase()
    }
    //每个次构造函数也可以委托给其他次造函数
    constructor(param1: String, param2 : String , classGoKotlin: ClassGoKotlin) : this(param1,classGoKotlin) {
        this.param2 = param2
    }

    constructor(param1: String, param2 : String ,param3 : String , classGoKotlin: ClassGoKotlin) : this(param1,param2,classGoKotlin){
        this.param2 = param2
    }

    constructor(param1: String, param2 : String ,param3 : String ,param4 : String, classGoKotlin: ClassGoKotlin) : this(param1,param2,param3,classGoKotlin){
        this.param3 = param3
    }
}

创建:

            var classGoKotlin0 = ClassGoKotlin("0")
            var classGoKotlin00 = ClassGoKotlin("0","可有可以无参数,看需求")
            var classGoKotlin1 = ClassGoKotlin("1", "2","4",ClassGoKotlin("0"))
            var classGoKotlin2 = ClassGoKotlin("1", "2","3","4",ClassGoKotlin("0"))
            var classGoKotlin3 = ClassGoKotlin("1", "2","3","4",ClassGoKotlin("1","2",ClassGoKotlin("0")))
...等等,灵活使用,建议还是不要太复杂,前三个就好,委托多了容易乱

与java区别:Kotlin没有new关键字,比Java更加简洁

2、继承

在 Kotlin 中所有类都有一个共同的超类 Any,这对于没有超类型声明的类是默认超类:

class Example // 从 Any 隐式继承

被继承的类前必须要加open关键字,才被允许继承。如果该类有一个主构造函数,其基类型可以(并且必须) 用(基类型的)主构造函数参数就地初始化例如:

open class ClassGoKotlin(param0 : String,var param00 : String = "赋默认初始值") {}

class ClassGoKotlinEx : ClassGoKotlin(""){}

如果类没有主构造函数,那么每个次构造函数必须使用 super 关键字初始化其基类型,或委托给另一个构造函数做到这一点。 注意,在这种情况下,不同的次构造函数可以调用基类型的不同的构造函数:

class ClassGoKotlinEx : ClassGoKotlin{
   //必须至少要有一个次构造函数,否则要在上面继承的时候就ClassGoKotlin("必须实现")
   constructor(param0 : String) : super(param0){
       //这个就是ClassGoKotlinEx(""),因为没有主构造函数,这个就类似主构造函数
   }
   constructor() : super("这里固定好值"){
       //这个就是ClassGoKotlinEx(""),因为没有主构造函数,这个就类似主构造函数
   }
   //这样就可以传递参数到继承的类里了
   constructor(param1 : String, classGoKotlinEx: ClassGoKotlinEx) : super(param1) {
       //经过我实际测试,这个次构造函数必须基于上面的构造函数才可以实现,否则ClassGoKotlinEx("", ClassGoKotlinEx());这样是不能调用的
   }
   //这个和上面第二个实际上是一样的,没有区别,可以删除
   constructor(classGoKotlinEx: ClassGoKotlinEx) : this(){
   }
}

使用上面的基础类:

//传入自定义数据
    var a = ClassGoKotlinEx("测试", ClassGoKotlinEx());
    //不传入数据,使用ClassGoKotlinEx类里设好的固定数据
    var b = ClassGoKotlinEx();

覆盖方法
Kotlin类覆盖继承类的方法,这里要求必须清晰,所有被继承的类如果需要被覆写,需要加上open关键字标识,就和需要继承的类需要加上open;所有被重写的方法需要override关键字标识,这样我们就非常的清晰那些类可以基础,那些方法可以覆盖,增强的可可读性,比java可读性强,例如:

//被继承类里声明方法
fun v(){}
open fun nv(){}

//继承类里覆盖方法
override fun nv(){}

Kotlin中,如果你想已经继承类的覆盖发放不在被再次覆盖的话,你只需要在覆盖方法前加final关键字即可,例如:

final override fun nv(){}

覆盖属性
和覆盖方法一样。
特别注意的是:
可以用一个 var 属性覆盖一个 val 属性,但反之则不行。这是允许的,因为一个 val 属性本质上声明了一个 getter 方法,而将其覆盖为 var 只是在子类中额外声明一个 setter 方法。
你可以在主构造函数中使用 override 关键字作为属性声明的一部分

覆盖规则
在 Kotlin 中,实现继承由下述规则规定:如果一个类从它的直接超类继承相同成员的多个实现, 它必须覆盖这个成员并提供其自己的实现(也许用继承来的其中之一)。 为了表示采用从哪个超类型继承的实现,我们使用由尖括号中超类型名限定的 super,如 super<Base>:

//类继承,方法覆盖规则
    open class A {
        open fun f() {}
        fun a() {}
    }

    interface B {
        fun f() {} // 接口成员默认就是“open”的
        fun b() {}
    }

    class C() : A(), B {
        // 编译器要求覆盖 f():
        override fun f() {
            super<A>.f() // 调用 A.f()
            super<B>.f() // 调用 B.f()
        }
    }

同时继承 A 和 B 没问题,并且 a() 和 b() 也没问题因为 C 只继承了每个函数的一个实现。 但是 f() 由 C 继承了两个实现,所以我们必须在 C 中覆盖 f() 并且提供我们自己的实现来消除歧义。

抽象类
类和其中的某些成员可以声明为 abstract。 抽象成员在本类中可以不用实现。 需要注意的是,我们并不需要用 open 标注一个抽象类或者函数——因为这不言而喻。+

我们可以用一个抽象成员覆盖一个非抽象的开放成员

//抽象类
open class Base {
    open fun f() {}
}

abstract class Derived : Base() {
    override abstract fun f()
}

同伴对象(Companion Object)
Koltin没有静态方法(static method),可以使用同伴对象代替,就实现了类似java静态方法的功能,只是这里同伴对象关键字大括号内所有方法都是同伴对象,类似java静态方法,例如:

//同伴对象(Companion Object)
    companion object {
        //companion object静态方法集合关键字
        fun Test1(): String? {
            return ""
        }
    }

封闭类
封闭类用来表示对类阶层的限制,可以限定一个值只允许是某些指定的类型之一,而不允许是其他类型。

要声明一个封闭类,需要将 sealed 修饰符放在类名之前,封闭类可以有子类,但所有的子类声明明都必须嵌套在封闭类的声明部分之内。

sealed class Expr {
    class Const(val number: Double) : Expr()
    class Sum(val e1: Expr, val e2: Expr) : Expr()
    object NotANumber : Expr()
}

从封闭类的子类再继承的子类(间接继承者)可以放在任何地方,不必在封闭类的声明部分之内。

好了,第一期就到这了,谢谢大家的观赏,敬请期待下一期《函数和Lambda表达式》

源码下载
这里源码会随着后面发布的Kotlin逐渐完善

    原文作者:小强彬
    原文地址: https://www.jianshu.com/p/904782a731db
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞