Kotlin进阶 - 类

这篇属于Kotlin进阶,讲了与类相关内容,主要涉及到:
类定义、类属性和函数、类继承、抽象类和接口、委托与代理、单例、数据类、伴生对象、运算符重载、枚举和密封类

1、面向对象入门,类的创建

示例:

//创建一个简单的类,该类有两个变量
class HelloKotlin(var letter:String ,var letter2: String)

fun main() {
    var demo = HelloKotlin("Hello"," Kotlin")

    println("${demo.letter}${demo.letter2}")
}

2、定义类的属性和方法

访问权限:默认为public,私有属性或方法用private修饰
示例:

package com.example.kotlin_demo
//创建一个简单的类,该类有两个变量
class HelloKotlin(var letter:String ,var letter2: String){
    /**
     * 方法的定义
     */
    fun method(){
        println("我是定义的一个方法")
    }
}

fun main() {
    var demo = HelloKotlin("Hello"," Kotlin")
    println("${demo.letter}${demo.letter2}")
    demo.method()
}

3、类继承

Kotlin中类默认为final类型,无法被继承,若想被继承和方法重写必须要对类或重写的方法加关键字open。如下示例:

//父类,
//有open修饰:允许其他类继承
//无open修饰:不允许其他类继承
open class Parent {
    //父类中的一个方法
    //有open修饰:允许子类重写
    //无open修饰:不允许子类重写
    open fun action(){
        println("Parent action----------")
    }
}

//Child类继承自Parent类
class Child : Parent() {
    //子类重写了父类的action方法
    override fun action(){
        println("Child action----------")
    }
}

4、抽象类和接口

抽象类用关键字abstract,定义如下:

/**
 * 抽象类
 */
abstract class AbstractDemo {
    //定义一个抽象方法
    abstract fun absAction()
}

接口使用关键字Interface,示例如下:

/**
 * 定义一个接口
 */
interface InterfaceDemo {
    //没有实现的方法
    fun action()
    //实现了方法
    fun action2(){
        println("我是action2。")
    }
}

抽象类和接口的使用,示例如下:

/**
 * 继承了抽象类AbstractDemo和接口InterfaceDemo
 */
class InheritDemo : AbstractDemo(),InterfaceDemo {
    //实现了抽象类中的抽象方法
    override fun absAction() {
        println("我来自抽象类中的抽象方法")
    }

    //实现了接口中的方法
    override fun action() {
        println("我来自接口中的action方法")
    }

    //重写了接口中的方法
    override fun action2() {
        super.action2()
        println("我来自接口中的实现的方法")
    }
}

注:
1.抽象类和接口已隐式声明为open类型,允许其他类继承,因此这里不需要显示的声明open。
2.抽象类中的非抽象方法若要让子类重写,需要加open标识。

5、Kotlin中实现代理和委托

使用关键字 by 来实现代理和委托

1.接口代理

针对实现了相同接口的两个类,如下示例:
接口类:

//定义了一个接口
interface IDelegate {
    fun delegateAction()
}

一个实现了IDelegate接口的DelegateImp.kt类

//继承了IDelegate接口
class DelegateImp1 : IDelegate {
    //实现了接口方法
    override fun delegateAction() {
        println("我来自Child,实现了接口中的delegateAction方法")
    }
}

定义了一个实现了代理接口类,通过by实现委托给DelegateImp1类实现IDelegate接口,如下:

//继承了IDelegate接口,并委托给Child类实现
//委托条件:继承自相同的接口
class ByDelegateImp : IDelegate by DelegateImp1() {

}
2.属性代理

自定义属性代理类:


import kotlin.reflect.KProperty
//定义一个属性代理类
class PropertyDelegate(var value:String) {
    //属性代理必须实现一个getValue方法
    operator fun getValue(any:Nothing?, property: KProperty<*>):String{
        return value
    }
    //属性代理必须实现一个setValue方法
    operator fun setValue(any:Nothing?, property:KProperty<*>, str:String){

    }
}

通过by使用这个属性代理类,如下:

fun main() {
    //通过by使用属性代理的方式
    var str : String by PropertyDelegate("我是代理类")
    println(str)
    str = "haha"
    println(str)
}

最终输出为:

我是代理类
我是代理类
3.系统中常见的委托属性

1.延迟属性(lazy properties): 其值只在首次访问时计算;
lazy() 是接受一个 lambda 并返回一个 Lazy 实例的函数,返回的实例可以作为实现延迟属性的委托: 第一次调用 get() 会执行已传递给 lazy() 的 lambda 表达式并记录结果, 后续调用 get() 只是返回记录的结果。

//使用lazy委托属性
val lazyValue :String by lazy {
    println("computed!")
    "Hello"
}
println(lazyValue)

输出内容为:
computed!
Hello

2.可观察属性(observable properties): 监听器会收到有关此属性变更的通知;
Delegates.observable() 接受两个参数:初始值与修改时处理程序(handler)。 每当我们给属性赋值时会调用该处理程序(在赋值后执行)。它有三个参数:被赋值的属性、旧值与新值:

class User {
    var name: String by Delegates.observable("<no name>") {
        prop, old, new ->
        println("$old -> $new")
    }
}

fun main() {
    val user = User()
    user.name = "first"
    user.name = "second"
}

输出值为:
<no name> -> first
first -> second

3.把属性储存在映射中
一个常见的用例是在一个映射(map)里存储属性的值。 这经常出现在像解析 JSON 或者做其他“动态”事情的应用中。 在这种情况下,你可以使用映射实例自身作为委托来实现委托属性。

class UserMap(val map: Map<String, Any?>) {
    val name: String by map
    val age: Int     by map
}

fun main() {
    val user = UserMap(mapOf(
            "name" to "John Doe",
            "age"  to 25
    ))

    println("name=${user.name},age=${user.age}")
}

输出为:
name=John Doe,age=25

系统属性委托参考自:kotlin-委托和属性委托

6、kotlin中单例

object
在Java中有类有静态方法,使用static关键字标识,在kotlin中则没有,但kotlin中有关键字object,通过该关键字标识类,即为单例,类中所有的方法都可通过类名直接访问,类似java中的静态方法。

//通过使用object标识为单例
object Singleton{
    fun log(str:String){
        println(str)
    }
}

fun main() {
    //直接通过类名调用方法,类似Java中的静态方法
    Singleton.log("我是单例中的log方法。")
}

输出为:
我是单例中的log方法。

companion object 伴生对象声明方式
通常情况下一个类中的方法只能通过类对象进行访问,但如何能像java中的类一样将部分方法声明为静态方法呢?答案就是companion object,通过使用该声明可以让直接通过类名调用方法。
伴生对象生成时机:①伴生对象函数或属性被调用;②类实例化时。
示例如下:

//Kotlin类中声明的方法都是非静态的
//但是有时候也会想把部分方法定义为静态,则可以使用companion object关键字进行标识
class CompanionObjectDemo {
    companion object{
        fun log(string: String){
            println("我来自伴生对象的方法---${string}")
        }
    }

    fun other(str : String){
        println(str)
    }
}

fun main() {
    //调用伴生对象的方法
    CompanionObjectDemo.log("companion function")

    var demo = CompanionObjectDemo()
    //调用对象中的方法
    demo.other("other function")
}

输出为:
我来自伴生对象的方法---companion function
other function

7、Kotlin中数据类(data修饰)

Kotlin中特有的一种类,采用关键字data进行修饰。

  • 数据类默认实现了超类(Any)的equals、hashCode、toString函数。
  • 提供了copy函数,类似Java的clone,拷贝的时候执行的是主构造函数,若其中一个变量是在次构造函数中赋值的,则无法copy过来,需要手动设置值。
  • 支持结构语法,即:val (x, y) = DataClassTest(10, 20)形式

定义的数据类需要满足以下条件:

  • 必须有至少带一个参数的主构造函数
  • 主构造函数的参数必须为val或者var,不能为临时变量
  • 数据类不能使用abstract、open、sealed和inner修饰;

使用场景
针对经常需要比较、复制或打印自身内容的类。
示例:

data class DataClassTest(var x: Int, var y: Int) {
    val isInvalidate = x > 0 && y > 0
}

fun main() {
    println(DataClassTest(10, 5))
    //data数据类默认提供了支持结构操作
    val (x, y) = DataClassTest(10, 20)
}

普通类中定义解构函数
定义方式
operator fun component1() = x
operator fun component2() = y
...
operator componentN() = n
主构造函数有几个参数就会生产对应几个component

/**
 * 普通类支持解构定义
 */
class Coor(var x: Int, var y: Int) {
    operator fun component1() = x
    operator fun component2() = y

}
fun main() {
    println(DataClassTest(10, 5))

    //普通类支持解构语法
    val (p,q) = Coor(10,20)
}

8、Kotlin类中运算符重载

Kotlin中支持对运算符进行重载,常见的运算符重载如下表

操作符 函数名 作用
+ plus 把一个对象添加到另一个对象里
+= plusAssign 把一个对象添加到另一个对象里,然后将结果赋值给第一个对象
- minus 把一个对象减去另一个对象
== equals 如果两个对象相等,则返回true,否则返回false
> compareTo 如果左边的对象大于右边的对象,则返回true,否则返回false
[] get 返回集合中指定位置的元素
.. rangeTo 创建一个range对象
in contains 如果对象包含在集合里,则返回true

使用示例如下:

class OperatorTest(var x: Int, var y: Int) {
    /**
     * 对“+”进行运算符重载
     */
    operator fun plus(other: OperatorTest) = OperatorTest(x + other.x, y + other.y)

    /**
     * 对"-"进行运算符重载
     */
    operator fun minus(other: OperatorTest) = OperatorTest(x - other.x, y - other.y)
}

fun main() {
    val op1 = OperatorTest(10, 20)
    val op2 = OperatorTest(5, 30)
    println(op1 + op2)
    println(op1 - op2)
}

9、Kotlin中的枚举类型(enum)和密封类(sealed)

1.枚举

跟java一样,kotlin中也有枚举类型,使用方式相同,示例如下:

//定义一个枚举类型
enum class EnumDemo {
    星期一,星期二,星期三,星期四,星期五,星期六,星期日
}

fun main() {
    //输出枚举类型的索引和值
    for (item in EnumDemo.values()){
        println("ordinal--${item.ordinal},name--${item.name}")
    }
}

输出结果为:
ordinal--0,name--星期一
ordinal--1,name--星期二
ordinal--2,name--星期三
ordinal--3,name--星期四
ordinal--4,name--星期五
ordinal--5,name--星期六
ordinal--6,name--星期日

2.密封类(sealed)

密封类,类似枚举,但是密封类主要是针对类的,密封类用来表示受限的类继承结构:当一个值为有限几种的类型、而不能有任何其他类型时。可以理解为枚举类型的扩展。
要声明一个密封类,需要在类名前面添加 sealed 修饰符。虽然密封类也可以有子类,但是所有子类都必须在与密封类自身相同的文件或者嵌套在密封类声明的内部中声明。原因:用sealed修饰的类是private访问类型

  • 方式一:嵌套在密封类内部声明
    示例:
//方式一:嵌套在密封类内部声明
sealed class SealedDemo {
    class Monday(var string: String) : SealedDemo(){
        fun mondayAct(){
            println("Monday--->${string}")
        }
    }
    class Tuesday(var string: String) : SealedDemo(){
        fun mondayAct(){
            println("Tuesday--->${string}")
        }
    }
}

fun main() {
    val monday = SealedDemo.Monday("星期一")
    val second = SealedDemo.Tuesday("星期二")
    monday.mondayAct()
    second.mondayAct()
}
  • 方式二:密封类声明在同一个文件中
    示例:
//方式二:密封类声明在同一个文件中
sealed class SealedDemo2
data class Const(val number: Double) : SealedDemo2()
data class Sum(val e1: SealedDemo2, val e2: SealedDemo2) : SealedDemo2()
object NotANumber : SealedDemo2()

fun main() {
    val const = Const(10.0)
    val sum = Sum(const,const)
}

枚举类型和密封类对比

维度 枚举 密封类
关键字 enum sealed
声明 嵌套在类里面 嵌套在类内部或声明在同一个文件中
类型 枚举常量都是一个对象 密封类针对的是类

欢迎留言大家互相交流学习!


示例源码地址kotlin_demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,826评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,968评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,234评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,562评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,611评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,482评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,271评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,166评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,608评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,814评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,926评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,644评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,249评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,866评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,991评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,063评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,871评论 2 354

推荐阅读更多精彩内容

  • 面向对象编程(OOP) 在前面的章节中,我们学习了Kotlin的语言基础知识、类型系统、集合类以及泛型相关的知识。...
    Tenderness4阅读 4,441评论 1 6
  • 扩展 扩展方法 Kotlin支持扩展方法和扩展属性。语法:被扩展的类/接口名.方法名() 父类不能使用子类的扩展方...
    AilurusFulgens阅读 295评论 0 1
  • 欢迎来到kotlin的世界,Kotlin 是一个用于现代多平台应用的静态编程语言,它可以编译成Java字节码,在J...
    依然范特稀西阅读 4,882评论 7 33
  • 前言 人生苦多,快来 Kotlin ,快速学习Kotlin! 什么是Kotlin? Kotlin 是种静态类型编程...
    任半生嚣狂阅读 26,209评论 9 118
  • 简介 Kotlin[https://github.com/JetBrains/kotlin] 是 JetBrain...
    Whyn阅读 671评论 0 1