美文网首页
Kotlin面向对象编程

Kotlin面向对象编程

作者: code希必地 | 来源:发表于2020-09-19 13:29 被阅读0次

    1、类与对象

    Kotlin中类的声明和Java类一样,都是用关键字class来声明类

    class Person {
        var name = ""
        var age = 0
    
        fun introduce() {
            Log.e("TAG", "我的名字叫" + name + " 我已经" + age + "岁了")
        }
    }
    

    Person类已经定义完成,那如何创建Person类的对象呢?

     val p = Person()
    

    和Java类似,只是剩去了new,这样也很好理解:Person()代表的是构造函数,而只有创建对象时才会调用构造函数,这样编码的意图也很明显就是创建对象。
    接下来我们就能使用对象进行一些操作:

    fun createPerson() {
            val p = Person()
            p.age=100
            p.name="张丹"
            p.introduce()
        }
    

    2、继承与构造函数

    • 1、在Kotlin中非抽象类、非接口默认是不能被继承的,要想某个类能被继承,就需要使用关键字open
    open class Person{
    }
    
    • 2、使用:来表示继承关系
    class Student :Person() {
    }
    

    细心的你肯定也发现了Person后加了一对小括号,这又是为什么呢?这就和Kotlin中的主构造函数和次构造函数有关系了,

    2.1、主构造函数

    • 1、Kotlin中每个类都默认有一个无参的构造函数来作为主构造函数,而且主构造函数有且仅有一个,次构造函数的个数不限。
    • 2、我们也可以给主构造函数显示的指定参数
    class Student(val sno: String, val grade: Int) : Person() {
    }
    

    我们将学号和年级在主构造函数中声明了,所以在创建Student对象的时候就必须传入主构造函数中要求的所有参数。

    • 3、主构造函数没有方法体,直接在类名后定义即可。
      如果我想在主构造函数中写一些逻辑该怎么办呢?Kotlin给我们提供了init结构体,所有主构造函数中逻辑可以写在结构体中。
    class Student(val sno: String, val grade: Int) : Person() {
        init {
            //在创建Student对象时就会执行这个结构体
            Log.e("TAG","我是主构造函数,学号sno:"+sno+" 年级grade:"+grade)
        }
    }
    
    • 4、在java中子类的构造函数必须要调用父类的构造函数,在Kotlin中也是如此。上面在继承Person时Person后面加了一对括号的用处就是在创建Student对象时指定在Student的构造函数中调用父类的那个构造函数。
      下面我们对Person修改一下,将name和age放在主构造函数中
    open class Person(val name: String, val age: Int) {
    }
    

    此时Student类肯定会报错,因为在Student类中我们指定调用父类的无参构造,而无参构造已经不存在了,必须给Person类传入name和age。可是在Student中并没有name和age,我们可以在Student中加入name和age

    class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age) {
    }
    
    • 5、注意:在Student类的主构造函数中加入的name和age不能使用val或var修饰,因为在主构造函数中使用val或var修饰的参数将自动变成该类的字段,这就导致和父类中的name、age同名导致的冲突。

    2.2、次构造函数

    • 1、其实在Kotlin中我们一般用不到次构造函数,因为Kotlin中提供了给函数设定参数默认值的功能,基本可以替代次构造函数。
    • 2、一个类只有一个主构造函数,但是可以有多个次构造函数。和主构造函数不同的是,次构造函数是有方法体的。
    • 3、Kotlin规定:当一个类中既有主构造函数又有次构造函数时,所有的次构造函数必须调用主构造函数(包含间接调用)
    class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age) {
    
        init {
            Log.e("TAG", "我是主构造函数,学号sno:" + sno + " 年级grade:" + grade)
        }
    
        constructor(name:String,age:Int) : this("",0,name,age) {
    
        }
    
        constructor():this("",0){
    
        }
    
    }
    
    • 4、次构造函数是通过关键字constructor定义的,这里我们定义了两个次构造函数:第一个次构造函数传入name和age作为参数,然后通过this关键字调用了主构造函数;第二个次构造函数没有参数,它通过this关键字调用了第一个次构造函数间接的调用了柱构造函数,这也是合法的。现在我们就有3种方式创建Student的实例了
    val stu1=Student("张无忌",50)
    stu1.introduce()
    
    val stu2=Student()
    stu2.introduce()
            
    val stu3=Student("10010",5,"尹力",130)
    stu3.introduce()
    
    • 5、如果一个类中没有显式的定义主构造函数,而使用关键字constructor定义了次构造函数,那么类中是没有主构造函数的
    class Teacher : Person {
        constructor(name: String, age: Int) : super(name, age) {
        }
    }
    

    注意这里代码的变化,Teacher类中没有显式的定义主构造函数并且定义了次构造函数,所以Teacher类是没有主构造函数,由于没有主构造函数,所以在继承Person类时也就不需要加扩号,原因很简单:由于Teacher类没有主构造函数,所以我们不能通过主构造函数来创建Teacher的实例,即使Kotlin允许此时添加括号,也是无法达到调用父类构造的目的。我们只能通过次构造函数创建实例,我们只能通过super来调用父类的构造函数。

    3、接口

    Kotlin中和Java一样都是单继承的,一个类只能继承一个父类,但是可以实现多个接口。和Java一样也是通过interface声明接口

    interface Study {
        fun readBook()
        fun doHomeWork()
    }
    

    Student类实现Study的接口,并实现其中的方法

    class Student(val sno: String, val grade: Int, name: String, age: Int) : Person(name, age),Study {
    
        init {
            Log.e("TAG", "我是主构造函数,学号sno:" + sno + " 年级grade:" + grade)
        }
    
        constructor(name: String, age: Int) : this("", 0, name, age) {
    
        }
    
        constructor() : this("", 0) {
    
        }
    
        override fun readBook() {
        }
    
        override fun doHomeWork() {
        }
    }
    
    • 1、Kotlin中实现接口和集成一样都是使用:,类和接口间使用,隔开,另外接口的后面不需要加括号,因为接口根本就没有构造可以被调用
    • 2、Kotlin中使用关键字override来重写父类的方法或实现接口中的方法,另外如果父类中的方法需要被重新的话,需要在方法前增加关键字open,否则无法重写。
    • 3、Kotlin中的接口可以有默认的实现,接口中有默认实现的方法子类可以不实现。
    interface Study {
        fun readBook(){
            Log.e("TAG","接口中的方法可以默认实现")
        }
        fun doHomeWork()
    }
    

    4、修饰符

    在Kotlin中函数的可见性修饰符相比于Java变动还是很大的。直接上表

    修饰符 Java Kotlin
    public 所有类可见 所有类可见(默认修饰符)
    private 当前类可见 当前类可见
    protected 当前类、子类、同一包下的类可见 当前类、子类可见
    default 当前类、统一包下类可见
    internal 同一模块下可见

    5、单例和数据类

    5.1、数据类

    数据类通常要重写equals()、hashCode()、toString()几个方法,其中eaquals()判断两个数据类是否相等,hashCode()作为eqauls()的配套方法,也需要重写否则会导致HashMap、HashSet中相关hash的操作不能正常工作,toString()是为了提供了一个清晰的日志输出,否则打印出来就是内存地址。下面举个简单的例子:

    public class CellPhone {
        String brand;
        double price;
    
        public CellPhone(String brand, double price) {
            this.brand = brand;
            this.price = price;
        }
    
        @Override
        public boolean equals(Object o) {
            if (o instanceof CellPhone) {
                CellPhone cellPhone = (CellPhone) o;
                return cellPhone.price == price && cellPhone.brand.equals(brand);
            }
            return false;
        }
    
        @Override
        public int hashCode() {
            return brand.hashCode() + (int) price;
        }
    
        @Override
        public String toString() {
            return "CellPhone:brand:" + brand + " price:" + price;
        }
    }
    

    在Kotlin的实现上述功能就只需一句代码即可。

    data class CellPhone(val brand: String, val price: Double) 
    

    神奇的地方就在于data关键字,在Kotlin中的类声明了data关键字,那么这个类就是一个数据类,Kotlin中会根据主构造函数中的参数自动生成eqauls()、hashCode()、toString()等方法并实现相应的逻辑。另外在一个类中没有任何代码时,可以省了大括号
    下面我们测试下该类

    fun testDataClass() {
            val cellPhone1 = CellPhone("xiaomi", 2000.0)
            val cellPhone2 = CellPhone("xiaomi", 2000.0)
            Log.e("TAG", "cellPhone1:" + cellPhone1.toString()+"  cellPhone2:"+cellPhone2.toString()+"  cellPhone1.equals(cellPhone2):"+(cellPhone1==cellPhone2))
        }
    
    输出:
    cellPhone1:CellPhone(brand=xiaomi, price=2000.0)  cellPhone2:CellPhone(brand=xiaomi, price=2000.0)  cellPhone1.equals(cellPhone2):true
    

    很明显CellPhone已经正常工作了。

    5.2、单例类

    在Java中的实现

    public class SingleInstance {
        private static SingleInstance instance;
    
        private SingleInstance() {
        }
    
    
        public static synchronized SingleInstance getInstance() {
            if (instance == null) {
                instance = new SingleInstance();
            }
            return instance;
        }
    
    }
    

    在Kotlin中创建一个单例类很简单,只需要将class关键字改成object关键字即可。

    object SingleInstance {
    }
    

    现在SingleInstance就是一个单例类了,我们可以直接在这个类中编写需要的函数,比如加入一个singleInstanceTest()

    object SingleInstance {
    
        fun singleInstanceTest() {
            Log.e("TAG","singleInstanceTest is called")
        }
    }
    

    可以看到,Kotlin不需要私有化构造函数,也不需要提供getInstance()这样的静态函数,只需要将class修改为object,Kotlin就帮我们生成了单例类相关的逻辑,它的调用也很简单

    SingleInstance.singleInstanceTest()
    

    这种写法看上去像是静态方法的调用,其实Kotlin内部已经帮我们生成了SingleInstance实例,并且该实例有且仅有一个。

    相关文章

      网友评论

          本文标题:Kotlin面向对象编程

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