Kotlin 学习笔记04

184 阅读4分钟

类 对象 接口

类继承结构

  • kotlin接口
    声明一个接口
interface Clickable{
    fun click()
}

实现这个接口

class Button :Clickable{
    override fun click() = print("I'm clicked") //这里的override是强制要求的不能省略
}

拥有默认实现方法的接口

interface Clickable{
    fun click()
    fun showOff() = println("I'm showOff")//默认实现方法
}

注意:如果多个继承出现了同名方法,如果不手动实现会报错,或者可以指定调用函数的方法如下:

interface Clickable{
    fun click()
    fun showOff() = println("I'm Clickable showOff")//默认实现方法
}
interface Focusable{
    fun showOff() = println("I'm Focusable showOff")
}

class Button :Clickable,Focusable{
    override fun showOff() {
        //调用focusable.showOff()方法,前提是showOff()方法已经实现,要不会报错
        //在Java中使用的是Focusable.super.showOff();
        super<Focusable>.showOff()
    }
    override fun click() = println("I'm clicked")
}

注意:kotlin在接口实现的方法,经过编译后,会生成一个接口和一个静态类,这个类用于实现接口
在kotlin中,所有方法修饰


重写在基类中定义的成员

open final 和abstract修饰符默认为final

SO: 如果你允许创建这个类的子类,需要使用open修饰

open class RightButton : Clickable { //声明一个可以被子类继承的类
    override fun click() {} // click的父类为 open,子类默认为open
    fun disable(){} //默认为final 子类不能继承
    open fun animate(){} //子类可以继承
}

Open类和智能转换

  • 智能转换只能是作用在,类型检查后没有改变过的变量上作用,也就是说只能在val上使用,并且是没有自定义访问器的类属性上使用。前提是,这个类必须是final的。kotlin默认为final修饰,在很多地方使用智能转换。很大程度上提高了代码表现力
  • abstract默认为open interface接口中只能是默认为open,且不能使用final修饰

可见修饰符 public

  • kotlin中,如果省略修饰符,默认是可见的//在Java中默认是私有的

  • internal 修饰符-》只在模块内可见 修正了Java中,封装容易被打破的弱点

  • 嵌套类和内部类在Java和kotlin中的关系

声明 Java kotlin
嵌套类(不存储外部类应用) static class A class A
内部类(保存外部类引用) class A inner class A
  • 密封类 sealed 如果在when处理所有用sealed修饰的类,就不需要提供默认分支。
sealed class Expr {
    class Num(val value: Int) : Expr()
    class Sum(val leftValue: Int, val rightValue: Int) : Expr()
}
//调用
fun evel(expr: Expr) {
    when (expr) {
        is Expr.Num -> println("num is ${expr.value}")
    }
}

密封类不能在类的外部拥有子类 sealed默认就有open属性

声明一个带非默认构造方法类或属性的类

关键字constructor,用于表示一个主构造方法

class User(val nikeName: String)
//显式构造方法
//初始化
class User(_nikeName: String){
    val nikeName = _nikeName //作者推荐使用下划线表示参数变量
}
//或者
class User(_nikeName: String){
    val nikeName: String
    init{//初始化代码块
        nikeName = _nikeName //作者推荐使用下划线表示参数变量
    }
}

//私有化构造方法
class User(var name: String, var age: Int, var address: String){
    private class constructor(name: String)
}
//通常会写成这样
class View private constructor(context: Context) { //私有构造方法
}

//委托构造方法
open class View {
    constructor(context: Context) : this(context, attributes = "attr")//委托给这个类另外一个构造方法
    constructor(context: Context, attributes: Attributes)
}

实现接口中声明的属性

interface User{
    val name: String
}
class PrivateUser():User{
    override val name: String
        get() = "Bob" //这里可以直接写函数,每次调用都会从get获取
}
//上面可以简写成
class PrivateUser(override val name: String) :User //在默认构造函数赋值,这里是只在初始化,赋值一次

通过gettersetter访问支持的属性


class User(private val name: String) {
    var address: String = "unspecified"
        set(value) {
            println("""
                Address is change for $name
                $field -> $value
            """.trimIndent())
            field = value
        }
}

数据类和委托类 by关键字

data class Client(val name: String,val postCode: Int)
//这里会自动生成方法
//toString()
//equls()
//hashCode()

注意:equls()hashCode是根据默认构造方法参数自动生成的,如果默认构造方法没有参数,则不会生成任何代码

数据类的不可变性 copy

创建副本是修改类的最好方法。

val client = Client("Bob",510000)
val clientCopy = client.copy()

类委托 by

  • 通常我们需要给一个类添加一些行为,常用方法是 装饰者模式
    本质就是创建一个类实现原始类所有的接口,并将原来的类作为一个字段保存,与原始类拥有同样的方法不需要修改,只需要转发就可以了。
    缺点:
    需要写很多的模版代码(在Java中)
    kotlin实现:
class countSet<T>(private val innerSet: MutableCollection<T> = HashSet()) : MutableCollection<T> by innerSet{//
    var objectAdd = 0

    override fun add(element: T): Boolean {
        objectAdd ++
        return innerSet.add(element)
    }

    override fun addAll(elements: Collection<T>): Boolean {
        objectAdd +=elements.size
        return innerSet.addAll(elements)
    }
}

object 关键字 核心思想:定义一个类,并创建这个类对象

主要用在以下场景:

  1. 对象生命 ->声明一个单例
  2. 伴生对象//可以访问类中所有属性,包括隐藏的属性
  3. 对象表达式,代替Java中的匿名类
class A{
    companion object {
        fun bar(){
            println("companion form A")
        }
    }
}

伴生对象是理想的工厂模式

class Employee{
    private val nickName:String
    constructor(email:String){
        nickName = email.substringBefore("@")
    }
    constructor(faceBooxId:Int){
        nickName = getFaceBookName(faceBooxId)
    }
}
//使用伴生对象优化可以这样写
class Employee private constructor(nickName: String) {
    companion object { //这里的伴生对象可以是匿名,也可以指定名字
        fun newSubcribingEmployee(email: String) = Employee(email.substringBefore("@"))
        fun newFaceBookUser(account: Int) = Employee(getFaceBookId(account))
    }
}

工程模式可以根据用途命名,非常有用,命名清晰避免创建新实例。

class Person(val name:String){
    companion object Loader{ //一般是省略不写
        fun formJson(name: String):Person{
            return Person(name)
        }
    }
}
//调用
println(Person.Loader.formJson("哈哈哈")) //Loader可以省略不写,因为每一个class只有一个伴生对象

匿名对象

eg:使用匿名对象实现事件监听

windows.addMouseListener{
    object :MouseAdapter(){
        override fun mouseClicked(e: MouseEvent?) {
            super.mouseClicked(e)
        }
        override fun mouseDragged(e: MouseEvent?) {
            super.mouseDragged(e)
        }
    }
}
  • kotlin匿名内不能,访问外部类不需要外部对象final声明,而且可以改变这个外部类的值
fun countClick(window: Window) {
    var clickCount = 0
    window.addMouseListener(object : MouseAdapter() { //这里的只需要写需要重新的方法
        override fun mouseClicked(e: MouseEvent?) {
            super.mouseClicked(e)
            clickCount++
        }
    })
}

kotlin的匿名内部类不需要重新所有的抽象方法,只需要重新自己需要重写部分即可

end