Android Kotlin(1)之《类》

作者: 小强彬 | 来源:发表于2017-06-28 11:13 被阅读0次

    大家好!这是我第三篇文章,之前说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逐渐完善

    相关文章

      网友评论

        本文标题:Android Kotlin(1)之《类》

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