Kotlin -面向对象

kotlin中的类

class Bird{
  val weight:Double = 500.0
  val color:String = "blue"
  val age:Int = 1
  fun fly(){}
}

上面代码反编译成Java的版本后,会有get方法,除此之外还有一些不同
1)不可变属性成员。这是利用java中的final修饰符来实现的,使用var声明属性则引用可变。
2)属性默认值。Java中的属性有默认值,kotlin中,除非显试的声明延迟初始化,不然就需要指定属性的默认值。
3)不同的可访问修饰符。kotlin中默认是public,而Java中默认是 default

可带有属性和默认方法的接口

Kotlin中的接口

interface Flyer{
  val speed:Int
  fun kind()
  fun fly(){println("I can fly")}
}

kotlin中的接口是不能像java那样直接赋值的 不过可以通过get方法来实现

interface Flyer{
    val speed:Int
      get() = 100
}

如果 接口中没有初始化,则需要在子类中重写进行初始化

class parot() :Bird {
    override val speed: Int
        get() =101
}

有了类就需要构造方法
kotlin中可以通过构造方法默认参数 实现构造方法重载
举个

class Bird(val weight:Double = 0.00,val age:Int = 1)

如果用Java来实现同样的效果 需要几个构造方法? 4个。
构造方法中可以用val 和var来声明构造方法的参数,val代表引用不可变 ,那么在构造方法中用val修饰这个变量还能被赋值吗,当然可以。声明后代表在类的内部声明了一个同名的属性,类似一下实现

class Bird(weight:Double = 0.0,age:Int= 0){
  val weight:Double
  val age: Int
  init{
      this.weight = weight
      this.age = age
}
}

上面的代码很有意思,说明kotlin也可以分开初始化,还有用val 修饰的变量是在初始化之后不可变。
init语句块 是构造方法的一部分,两者在表现形式上是分离的,如果在初始化时进行其他的额外操作,那么我们就可以使用init语句块来执行 。

构造方法的参数可以在init语句中访问,也可以用于初始化类内部的属性成员的情况

class Bird(weight:Double = 0.00){
    val weight:Double = weight
}

但是不能在成员函数中访问

class Bird(weight:Double){
  fun printWeight(){
    print(weight) // 这里不能直接访问 weight
}
}

主从构造方法

我们可能需要从一个特殊的数据来获取构造类的参数值,这时候如果可以定义一个额外的构造方法,接收一个自定义的参数会显的特别方便

class Bird(val age:Int){
 constructor(birth:DateTime) :this(getAgeByBirth(birth)){}
}

类外部定义的构造方法被称为主构造方法,每个类可最多存在一个主构造方法,和多个从构造方法,如果主构造方法存在注解或者可见星修饰符,则constructor 关键字不可省略。从构造方法会先执行对其他构造方法的委托,然后执行自身代码块的逻辑; 如果存在主构造方法 那么每个从构造方法都要直接或者间接的委托给它。

不同的访问控制原则

继承虽然灵活,但是如果被滥用会引起一些问题。举个 我认为企鹅也是一种鸟, 于是声明了一个Penguin类来继承Bird

open class Bird{
  open fun fly(){ print("I can fly.")}
}
class Penguin :Bird(){
  override fun fly(){
    print("I can't  fly actually")
}
}

kotlin中的继承是用:,kotlin中类和方法默认是不可被继承或重写的,所以必须加上open 修饰符。上面的例子明显不太好,所以kotlin中默认类和方法都是加了final修饰的

sealed

用sealed修饰的类 不能被初始化,因为他背后是基于一个抽象类实现的,若要继承则需要将子类定义在用一个文件中(同一个包也不行,就是同一个文件)

可见性修饰符

  1. kotlin与Java的默认修饰符不同,kotiin中是public ,而Java中是default
    2)kotlin中有个独特的修饰符internal (模块内可见)
    3)kotlin可以在一个文件内单独声明方法以及常量,同样支持可见性修饰
    4)Java中除了内部类可以用private修饰以外,其他类都不允许private修饰,而kotlin可以
    5)protected的访问访问范围不同,Java中是包,类子类,而kotlin中只允许类及子类。

模块内访问(internal)

  • 一个Eclipse项目
  • 一个Intellij IDEA项目
  • 一个Maven项目
  • 一个Grandle项目
  • 一组由一次Ant任务执行编译的代码

inner

kotlin中的内部类默认是static 的,所以想要定义一一个内部类 要加上inner关键字
(如果不加则没有外部类的引用,无法访问外部类的属性和方法)

通过委托来代替多继承实现需求

interface CanFly{
    fun fly()
}

interface CanEat{
    fun eat()
}

class Flyer:CanFly {
    override fun fly() {
        println("i can fly")
    }
}

class  Animal: CanEat{
    override fun eat() {
        println(" i can eat")
    }
}

class Bird(flyer:Flyer,animal: Animal) :CanFly by flyer ,CanEat by animal

object Test{
    @JvmStatic
    fun main(args: Array) {
        val flyer = Flyer()
        val animal = Animal()
        val bird = Bird(flyer,animal)
        bird.eat()
        bird.fly()
    }
}

疑问:首先,委托方式怎么跟接口实现多继承如此相似,而且好像也没有简单多少,其次,这种方式好像跟组合也很像,那么它到底有什么优势呢?只要有以下亮点:
1)接口是无状态的,所以即使它提供了默认方法实现也是很简单的,不能实现复杂逻辑(也不推荐) 我们可以利用上面委托的这种方式,虽然他也是接口委托,但是是用一个具体的类去实现方法逻辑,可以拥有更强大的逻辑
2)假设我们需要继承的类是A,委托对象是B,C,我们在具体调用的时候并不是像组合一样A.B.method,而是可以直接调用A.medthod,这样更能表达A拥有该mehod的能力,更加直观

真正的数据类

有这样一种场景,我们并不想要那么强大的类,也许我们只是想要单纯的使用类来封装数据,类似于Java中的DTO(Data transfer object)的概念,但是我们知道在Java中显的繁琐,因为通常情况下我们会声明一个javaBean,然后定义一堆getter和setter。但是你很可能已经厌烦了这些冗长的代码,那来看看kotlin是如何改进这个问题的吧

用data class 创建数据类

data class 顾名思义就是数据类

data class Bird(var weight:Double ,var age:Int,var color:String)

那么 这个data关键字帮我们做了哪些事情
1)提供get set方法
2)重写了toString 方法,重写equals 和hashcode
3)提供了一些方法 如 copy 和componentN

        val b1 = Bird(3.0,1)
        val b2 = b1.copy(age = 2)

copy 内部实现是通过传过来的参数重新new了一个对象
接下来我们看看componentN方法。简单来说,componentN可以理解为类属性的值,其中N代表属性的顺序,比如component1代表第一个属性的值,那么这样设计到底有什么用呢? 我们来思考一个问题,我们或多或少的知道怎么将属性绑定到类上,到那时如何将类的属性绑定到相应变量上却不是很熟悉 如:

 // 普通方式
        val b1 = Bird(3.0,1,Color.BLACK)
        val weight = b1.weight
        val age = b1.age
        val color = b1.color
        
        //kotlin 进阶
        val (weight,age,color) = b1

只有提供了component函数的时候才可以这么写
看到kotlin的语法信息你一定会感到兴奋,因为你可能写过类似下面的代码

String birdInfo = "20.0,1,bule";
String[] temps = birdInfo.split(".")
double weight = Double.valueOf(temps[0]);
int age = Integer.valueOf(temps[1]);
String color = temps[2];

这样的代码有时真的很繁琐,我们明明知道值的情况,却要分好几步来给变量赋值,很幸运,kotlin提供了更优雅的做法:

val (weight,age,color) = birdInfo.split(".")

这里的原理就是解构,通过编译器的约定实现解构,kotlin对数组的结构有一定限制,在数组中它默认最多允许赋值5个变量
自己实现对应属性的componentN方法,如:

// 
data class Bird(val weight:Double,val age:Int,val color: Color){
    var sex = 1
    operator fun component4() = sex

    constructor(weight: Double,age: Int, color: Color,sex:Int):this(weight, age, color){
        this.sex = sex
    }

}

//使用
val b1 = Bird(3.0,1,Color.BLACK,1)
val (weight,age,color,sex) = b1

除了数组支持解构外,kotlin也提供了其他常用的数据类,分别是Pair和Triple,使用如下

val(weightT,ageT,colorT) = Triple(20.0,1,Color.BLUE)

伴生对象

半生对象的设计目的就是为了把普通变量和静态变量区别开,而不是混在一个类中靠static关键字区别。

关于object关键字

两个作用
1.天生的单例(饿汉式,线程安全)
2.写匿名内部类

你可能感兴趣的:(Kotlin -面向对象)