Kotlin-类和接口

作者: CoorChice | 来源:发表于2017-05-25 18:38 被阅读1377次

    类的声明

    同Java、Python等一样,Kotlin使用class关键字来声明一个类。

    class Skill {
        
    }
    

    创建一个对象实例:

    val fireBallSkill = Skill()
    

    Kotlin中创建对象不需要new

    构造函数

    主要构造函数

    class Skill public constructor(var name: String = "未知") {
       
    }
    

    Kotlin的主构造函数很特别,就像上面那样。既然是函数,我们同样可以为它指定默认值。参见【Kotlin-函数:http://www.jianshu.com/p/3a815c51dcb5】。如果你不需要可见性修饰符(不写表示public),可以省略constructor。关于可见性修饰符参见【Kotlin基础语法:http://www.jianshu.com/p/bc8896914df8】。那么,上面的类声明将会简化成这样:

    class Skill(var name: String = "未知") {
       
    }
    

    如果你足够细心的话,也许你已经注意到了在 【主构造函数】 的参数前有一个 var 关键字。函数可不能这么搞!这又是Kotlin迷人的特性之一。通过这种主构造函数的语法,就可以完成对属性的初始化。

    它相当于:

    class Skill(name: String = "未知") {
       var name = name
    }
    

    当然,这种特殊的主构造函数写法存在一个大问题!不知道你想到没?...不能在主构造函数里写代码了啊!WTF?

    别忙着喷,Kotlin没那么坑啦。看看怎么在初始化时添加逻辑。

    class Skill(var name: String = "未知") {
    
       init{
           do something...
       }
    }
    

    init{ } 块中的代码会在初始化时被调用。虽然看起来有些奇怪,但是习惯就好。就像 static{ } 一样喽。

    次要构造函数

    在Kotlin中,次要构造函数有一个恶心的规则,++就是要显示委托给主构造函数(在主构造有至少一个参数的时候)++。看看怎么回事:

    class Skill public constructor(var name: String = "未知") {
    
        var mp: Int? = 0
        
        // 次要构造函数
        constructor(name: String, mp: Int?) : this(name) {
            this.mp = mp
        }
    }
    

    看,次要构造函数不得不显示的通过this(name)委托给主构造函数。当然,如果主构造函数没有参数,能够简化不少。

    class Equipment {
    
        constructor(header: String, clothes: String) {
        
        }
    }
    

    继承

    在Kotlin中,所有的类都会隐式的继承Any,就像Java中所有的类都继承Object一样。

    Kotlin在设计的时候遵循了《Effective Java》中的 第 17 条:++要么为继承而设计,并提供文档说明,要么就禁止继承。++ 所以Kotlin的一个类及其函数,默认是不可以继承。如果需要继承,必须加上open关键字。

    open class Skill(var name: String = "未知") {
        var damage: Int? = 0
        var description: String? = "当心!这个技能强大到可能会伤到你自己!"
        var mp: Int? = 0
        constructor(name: String, mp: Int?) : this(name) {
            this.mp = mp
        }
        open fun effect() {
        }
    }
    

    Skill类和它的成员函数都想继承、重载的话,给它们加上open去吧。就上上面的代码一样。

    接下来,看看如何继承它。

    class FireBallSkill : Skill {
        constructor(name: String) : super(name)
        constructor(name: String, mp: Int?) : super(name, mp)
    
        override fun effect() {
            super.effect()
        }
    }
    

    继承的格式就是号后跟基类。并且,构造函数需要使用super关键字进行基类的初始化。

    重载方法Kotlin提供了关键字,不必要再用注解了。就像上面重载effect()函数一样。如果不想让FireBallSkilleffect()再被继承,只需要再添加final关键字即可。

    对于像Skill这样有主构造函数的基类来说,还可以这样来写继承。

    class FireBallSkill(name: String) : Skill(name) {
        constructor(name: String, mp: Int?) : this(name)
    
        override fun effect() {
            super.effect()
        }
    }
    

    这种情况下,你可以在子类的构造函数中初始化基类。那么,次要构造函数就可以直接委托给子类自己的构造函数进行初始化。

    需要注意,Kotlin有一个规则,就是一个子类的两个以上的超类中如果包含同名open函数,那么这个函数必须被重载,否则不知道该调用那一个函数。用个官方例子说明吧:

    open class A {
        open fun f() { print("A") }
        fun a() { print("a") }
    }
    
    interface B {
        fun f() { print("B") } // 接口成员默认就是“open”的
        fun b() { print("b") }
    }
    
    class C() : A(), B {
        // 编译器要求覆盖 f():
        override fun f() {
            super<A>.f() // 调用 A.f()
            super<B>.f() // 调用 B.f()
      }
    }
    

    注意一点,Kotlin的多继承(继承类、实现接口)使用,分隔。

    属性也可以重载

    在Kotlin中,成员属性是可以重载的。当然,需要使用open关键字。

    open class Skill(var name: String = "未知") {
        open var description: String? = "当心!这个技能强大到可能会伤到你自己!"
    
    }
    

    和重载方法一样,Kotlin使用override关键字重载属性。

    class FireBallSkill(name: String) : Skill(name) {
        override var description: String? = "纵火狂热分子最爱使用的技能。"
    }
    

    抽象类

    同Java一样,Kotlin也使用abstract关键字来定义抽象类。

    abstract class Actor{
        abstract fun move()
    }
    
    class Hero: Actor() {
        override fun move() {
        }
    }
    

    对于抽象类,open关键字可以省略。很明显,抽象类就是为了继承。

    object关键字

    在Kotlin中,object关键字被用来声明一些特殊对象或类。

    • 匿名内部类
    tv_test.setOnClickListener(object : View.OnClickListener {
        override fun onClick(v: View?) {
        }
    })
    

    在Kotlin中,匿名内部类需要使用object关键字来申明,在:后跟超类。既然是匿名,千万不要尝试在obejct后加名字!

    • 实现单例
      单例模式的写法从未如此简单和安全。Java中各种繁杂的单例写法可以出门右转了。
    object WeatherManager{
    
        fun rain(){
    
        }
    
        fun sun(){
    
        }
    }
    

    只需一个object关键字声明即可。看看如何使用单例。

    WeatherManager.rain()
    WeatherManager.sun()
    

    简便的,直接用类名就可以调用。有点像在调用静态方法,但是单例对象像纯真的孩子口中的话一样真实的创建了。

    • 伴生对象
      如果上面的单例写在一个类中,并且加上companion关键字,那么这个单例就成为宿主类的伴生对象了。伴生对象是用来代替类似Java中的静态变量的,真正意义上的实现面向对象
    open class Hero: Actor() {
        override fun move() {
        }
        companion object Factory{
            fun create(): Hero = Hero()
        }
    }
    

    事实上,你还可以把伴生对象的名称Factory省略掉。看看如何使用。

    val demoHero = Hero.create()
    

    调用伴生对象的成员,就像在Java中调用类的静态成员一样。

    枚举类

    在Kotlin中,使用enum关键字将一个类声明为枚举类。

    enum class Color(val rgb: Int) {
            RED(0xFF0000),
            GREEN(0x00FF00),
            BLUE(0x0000FF)
    }
    

    相比Java,多了个class关键字。

    数据类

    public class User{
    
        private String name;
        private int age;
        
        public void setName(String name){
            this.name = name;
        }
        
        public String getName(){
            return name;
        }
        
        public void setAge(int age){
            this.age = age;
        }
        
        public int getAge(){
            return age;
        }
    }
    

    这是Java中令人烦躁的数据类声明过程。是的,我们都特别怕写,即使在有工具帮忙的情况下,看着这么庞大的一个类也是让人难受的。看看Kotlin如何简化的吧。

    data class User(var name: String, var age: Int)
    

    使用data关键字,一行代码生成一个数据类。还有什么理由不爱Kotlin呢?

    接口

    上面你也看到了接口的运用,我们同样使用interface关键字来定义接口。注意,Kotlin中的接口和Java中有不小的差别。因为Kotlin中的接口是可以有方法体的!当然,有方法体的你不用去实现它。此外,Kotlin中的接口还能包含属性,当然它们是抽象的。你必须在实现接口的时候也实现这些个抽象属性。

    interface Attack{
        var damage: Int
    
        fun attack()
    
        fun preRock(){
    
        }
    
        fun aftRock(){
    
        }
    }
    

    看一个实例吧。

    class Mage: Attack{
        //在这实现了抽象属性
        override var damage: Int
            get() = 0
            set(value) {}
    
        override fun attack() {
        }
    
    }
    

    相关文章

      网友评论

        本文标题:Kotlin-类和接口

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