User-Profile-Image
hankin
  • 5
  • Java
  • Kotlin
  • Spring
  • Web
  • SQL
  • MegaData
  • More
  • Experience
  • Enamiĝu al vi
  • 分类
    • Zuul
    • Zookeeper
    • XML
    • WebSocket
    • Web Notes
    • Web
    • Vue
    • Thymeleaf
    • SQL Server
    • SQL Notes
    • SQL
    • SpringSecurity
    • SpringMVC
    • SpringJPA
    • SpringCloud
    • SpringBoot
    • Spring Notes
    • Spring
    • Servlet
    • Ribbon
    • Redis
    • RabbitMQ
    • Python
    • PostgreSQL
    • OAuth2
    • NOSQL
    • Netty
    • MySQL
    • MyBatis
    • More
    • MinIO
    • MegaData
    • Maven
    • LoadBalancer
    • Kotlin Notes
    • Kotlin
    • Kafka
    • jQuery
    • JavaScript
    • Java Notes
    • Java
    • Hystrix
    • Git
    • Gateway
    • Freemarker
    • Feign
    • Eureka
    • ElasticSearch
    • Docker
    • Consul
    • Ajax
    • ActiveMQ
  • 页面
    • 归档
    • 摘要
    • 杂图
    • 问题随笔
  • 友链
    • Spring Cloud Alibaba
    • Spring Cloud Alibaba - 指南
    • Spring Cloud
    • Nacos
    • Docker
    • ElasticSearch
    • Kotlin中文版
    • Kotlin易百
    • KotlinWeb3
    • KotlinNhooo
    • 前端开源搜索
    • Ktorm ORM
    • Ktorm-KSP
    • Ebean ORM
    • Maven
    • 江南一点雨
    • 江南国际站
    • 设计模式
    • 熊猫大佬
    • java学习
    • kotlin函数查询
    • Istio 服务网格
    • istio
    • Ktor 异步 Web 框架
    • PostGis
    • kuangstudy
    • 源码地图
    • it教程吧
    • Arthas-JVM调优
    • Electron
    • bugstack虫洞栈
    • github大佬宝典
    • Sa-Token
    • 前端技术胖
    • bennyhuo-Kt大佬
    • Rickiyang博客
    • 李大辉大佬博客
    • KOIN
    • SQLDelight
    • Exposed-Kt-ORM
    • Javalin—Web 框架
    • http4k—HTTP包
    • 爱威尔大佬
    • 小土豆
    • 小胖哥安全框架
    • 负雪明烛刷题
    • Kotlin-FP-Arrow
    • Lua参考手册
    • 美团文章
    • Java 全栈知识体系
    • 尼恩架构师学习
    • 现代 JavaScript 教程
    • GO相关文档
    • Go学习导航
    • GoCN社区
    • GO极客兔兔-案例
    • 讯飞星火GPT
    • Hollis博客
    • PostgreSQL德哥
    • 优质博客推荐
    • 半兽人大佬
    • 系列教程
    • PostgreSQL文章
    • 云原生资料库
    • 并发博客大佬
Help?

Please contact us on our email for need any support

Support
    首页   ›   Kotlin   ›   Kotlin Notes   ›   正文
Kotlin Notes

Kotlin-类型初步—类和接口(七)

2021-01-12 01:31:27
1392  0 0
参考目录 隐藏
1) 类
2) 构造函数
3) 作为辅助构造函数
4) 工厂函数
5) 创建类的实例
6) 接口
7) 在一个类中实现两个或多个接口
8) 解决重写冲突(多接口)
9) 抽象类
10) Getter 和 Setter
11) 示例:更改属性的值
12) 属性引用
13) 完整例子:

阅读完需:约 11 分钟

类

Kotlin中的类是使用class关键字来声明:

class Invoice {

}

类声明由类名,类头(指定类型参数,主构造函数等)和类体组成,由大括号括起来。类头和类主体都是可选的; 如果类没有主体,可以省略花括号。如下 – 

class Invoice 

在java中变量可以不用初始化,但是kotlin中必须要初始化,因为要避免null异常。


构造函数

Kotlin中的类可以有一个主构造函数和一个或多个辅助构造函数。 主构造函数是类头的一部分:它在类名后面(和可选的类型参数)。

class Person constructor(firstName: String) {
}

如果主构造函数没有任何注释或可见性修饰符,那么可以省略constructor关键字:

class Person(firstName: String) {
}

主构造函数不能包含任何代码。 初始化代码可以放在初始化程序块中,前缀为init关键字:

class Customer(name: String) {
    init {
        logger.info("Customer initialized with value ${name}")
    }
}

初始化程序块中可以使用主构造函数的参数。 它们也可以用在类体中声明属性的初始化器:

class Customer(name: String) {
    val customerKey = name.toUpperCase()
}

实际上,要声明属性并从主构造函数初始化它们,Kotlin有一个简洁的语法:

加上var 或 val 同时定义构造器的参数和属性,而不加则只是构造器的参数不是属性。

class Person(val firstName: String, val lastName: String, var age: Int) {
    // ...
}

与常规属性大体相同,主构造函数中声明的属性可以是多值(var)或只读(val)。
如果构造函数具有注释或可见性修饰符,则constructor关键字是必需的,修饰符将在它之前:

class Customer public @Inject constructor(name: String) { ... }

作为辅助构造函数

类还可以声明辅助构造函数,它们以constructor关键字作为前缀:

class Person {
    constructor(parent: Person) {
        parent.children.add(this)
    }
}

如果不定义主构造器,而定义一个或多个副构造器,那么容易出现类初始化有多条,很紊乱,所以我们可以去继承父类的构造器,如果父类只有一个默认无参的构造器的话可以将 :super() 省略掉。

如果不去调用父类的构造器也可以去调用本类的其他构造器

如果有重载的需求,那么在主构造器上用默认参数就可以啦,这是比较推荐的写法,当然如果想让java代码看懂kotlin,可以加上这个注解。


如果类具有主构造函数,则每个辅助构造函数需要通过另一个辅助构造函数直接或间接地委派给主构造函数。 使用this关键字对同一类的另一个构造函数进行委派:

class Person(val name: String) {
    constructor(name: String, parent: Person) : this(name) {
        parent.children.add(this)
    }
}

这个this的调用和上面的子类调用父类的构造器以及子类调用自身的其他构造器的原理是一样的。


如果一个非抽象类没有声明任何构造函数(主或辅助),那么它将不使用参数来生成主构造函数。 构造函数的可见性将是公开的。 如果不希望类具有公共构造函数,则需要声明具有非默认可见性的空主构造函数:

class DontCreateMe private constructor () {
}

在JVM上,如果主构造函数的所有参数都具有默认值,编译器将生成一个额外的无参数构造函数,它将使用默认值。 这使得更容易使用Kotlin与诸如Jackson或JPA的库,通过无参数构造函数创建类实例。


工厂函数

构造同名的工厂函数,函数名也可以与类的名字不同,工厂方法比构造函数更具有表现力,因为它可以通过名字知道类是如何构造出来的

class Person(var age: Int, var name: String = "unknown") {
    override fun equals(other: Any?) = (other as? Person)?.name?.equals(name) ?: false
    override fun hashCode() = name.hashCode()
}

val persons = HashMap<String, Person>()
//函数名也可以与类的名字不同
fun Person(name: String): Person {
    return persons[name]
        ?: Person(1, name).also { persons[name] = it }
}

类似String有常见的工厂方法调用:

fun main() {
    val str = String()//调用的是构造函数
    val str1 = String(charArrayOf('1', '2'))//调用的是工厂函数 实际是一个函数指向了String的构造函数
}

当然我们也可以自己给系统String类添加工厂方法,一般写成函数名跟类名一样

fun String(ints: IntArray): String {
    return ints.contentToString()
}

创建类的实例

要创建一个类的实例,需要调用类的构造函数,就像它是一个常规函数一样


接口

Kotlin接口类似于Java 8中的接口。它们可以包含抽象方法的定义以及非抽象方法的实现。但是,它们不能包含任何状态。

interface MyInterface {

    var test: String   //抽象属性

    fun foo()          //抽象方法
    fun hello() = "Hello there" //具有默认实现的方法
}
  • 创建接口 MyInterface。
  • 该接口有一个抽象属性 test 和一个抽象方法 foo()。
  • 该接口还具有非抽象方法 hello()。

interface MyInterface {

    val test: Int   //抽象属性
    fun foo() : String   //抽象方法(返回字符串)
    fun hello() {   //具有默认实现的方法
        // body (optional)
    }
}

class InterfaceImp : MyInterface {

    override val test: Int = 25
    override fun foo() = "Lol"

    //其他代码
}

在kotlin中 :是有很多用途的,其中 :用来表示继承和实现类

接口还可以具有提供访问器实现的属性

interface MyInterface {

    //带实现的属性
    val prop: Int
        get() = 23
}

class InterfaceImp : MyInterface {
    //类主体
}

fun main(args: Array<String>) {
    val obj = InterfaceImp()

    println(obj.prop)
}

运行该程序时,输出为:23

这里,prop 不是抽象的,但是它在接口中是有效的,因为它提供了访问器的实现。

但是,您不能在接口内部执行类似 val prop:Int = 23 的操作。

在一个类中实现两个或多个接口

Kotlin不允许真正的多重继承。但是,可以在一个类中实现两个或多个接口

interface A {

    fun callMe() {
        println("来自接口A")
    }
}

interface B  {
    fun callMeToo() {
        println("来自接口B")
    }
}

//实现两个接口A和B
class Child: A, B

fun main(args: Array<String>) {
    val obj = Child()

    obj.callMe()
    obj.callMeToo()
}

运行该程序时,输出为:

来自接口A
来自接口B

解决重写冲突(多接口)

假设两个接口(A和B)具有相同名称的非抽象方法(假设callMe()方法)。您在一个类中实现了这两个接口(假设C)。 现在,如果使用 C 类的对象调用callMe()方法,则编译器将引发错误。 例如

interface A {

    fun callMe() {
        println("接口 A")
    }
}

interface B  {
    fun callMe() {
        println("接口 B")
    }
}

class Child: A, B 

fun main(args: Array<String>) {
    val obj = Child()

    obj.callMe()
}

要解决此问题,需要提供自己的实现。

interface A {

    fun callMe() {
        println("接口 A")
    }
}

interface B  {
    fun callMe() {
        println("接口 B")
    }
}

class C: A, B {
    override fun callMe() {
        super<A>.callMe()
        super<B>.callMe()
    }
}

fun main(args: Array<String>) {
    val obj = C()

    obj.callMe()
}

现在,当您运行程序时,输出将是:

接口  A
接口  B

这里,在 C 类中提供了callMe()方法的显式实现。

语句 super<A>.callMe()调用类A的callMe()方法。类似地,super<B>.callMe()调用类B的callMe()方法。


抽象类

与Java一样,abstract 关键字用于在Kotlin中声明抽象类。无法实例化抽象类(不能创建抽象类的对象)。但是,您可以从它们中继承子类。

除非您明确使用 abstract 关键字将其抽象,否则抽象类的成员(属性和方法)是非抽象的。让我们举个实例:

abstract class Person {
    
    var age: Int = 40

    fun displaySSN(ssn: Int) {
        println("我的社保号是: $ssn.")
    }

    abstract fun displayJob(description: String)
}
  • 创建一个抽象类 Person 。您不能创建该类的对象。
  • 该类具有非抽象属性 age和非抽象方法 displaySSN()。如果您需要在子类中覆盖这些成员,则应使用 open 关键字标记它们。
  • 该类具有抽象方法 displayJob()。它没有任何实现,必须在其子类中重写。

重点:在抽象类中,如果没有用open关键字来标记,或者不是 abstract 方法那么它将无法被重写。

注意:抽象类总是开放的。 您不需要显式使用open关键字从它们继承子类。

abstract class Person(name: String) {

    init {
        println("我的名字是 $name.")
    }

    fun displaySSN(ssn: Int) {
        println("我的社保号是 $ssn.")
    }

    abstract fun displayJob(description: String)
}

class Teacher(name: String): Person(name) {

    override fun displayJob(description: String) {
        println(description)
    }
}

fun main(args: Array<String>) {
    val jack = Teacher("Jack Smith")
    jack.displayJob("我是一名数学老师。")
    jack.displaySSN(23123)
}

Getter 和 Setter

在编程中,getter 用于获取属性的值。同样,setter 用来设置属性的值。

在Kotlin中,getter 和 setter是可选的,如果未在程序中创建它们,它们将自动生成。

Kotlin中的以下代码

class Person {
    var name: String = "defaultValue"
}

等同于

class Person {
    var name: String = "defaultValue"

    // getter
    get() = field

    // setter
    set(value) {
        field = value
    }
}

示例:更改属性的值

fun main(args: Array<String>) {

    val maria = Girl()
    maria.actualAge = 15
    maria.age = 15
    println("玛莉亚: 实际年龄  = ${maria.actualAge}")
    println("玛莉亚: 虚拟年龄  = ${maria.age}")

    val angela = Girl()
    angela.actualAge = 35
    angela.age = 35
    println("安戈洛: 实际年龄  = ${angela.actualAge}")
    println("安戈洛: 虚拟年龄  = ${angela.age}")
}

class Girl {
    var age: Int = 0
    get() = field
    set(value) {
        field = if (value < 18)
            18
        else if (value >= 18 && value <= 30)
            value
        else
            value-3
    }

    var actualAge: Int = 0
}

属性引用

在kotlin中可以用 :: 来引用类里的内容

class Person(age: Int, name: String) {
    var age: Int = age //property
        get() {
            return field
        }
        set(value) {
            println("setAge: $value")
            field = value
        }
    var name: String = name
        get() {
            return field // backing field
        }
        set(value) {

        }
}

fun main() {
    val ageRef = Person::age // 没有被实例化就要用类名::方法名
    val person = Person(18, "Bennyhuo")
    val nameRef = person::name
    ageRef.set(person, 20)
    nameRef.set("Andyhuo")
}

完整例子:

AbsClass 抽象类

abstract class AbsClass {
    abstract fun absMethod()
    open fun overridable(){}
    fun nonOverridable(){} // 不能被重写因为没有open或者abstract
}

SimpleInf 接口

interface SimpleInf {
    val simpleProperty: Int // property

    fun simpleMethod()
}

SimpleClass 实现和继承的类

open class SimpleClass(var x: Int, val y: String)
    : AbsClass(), SimpleInf {
// override 代表了这是重写的
    override val simpleProperty: Int 
        get() {
            return 2
        }

    val z : Long
        get() {
            return simpleProperty * 2L
        }

    override fun absMethod() {}
    override fun simpleMethod() {}
    fun y(){}

    fun zzz(string: String){

    }

    final override fun overridable(){

    }
}

// 这个类可以继承 SimpleClass 是因为 SimpleClass类前面加上了 open 不然无法继承
// 但是如果不想让某一个方法被重写那么加上 final 比如:final override fun overridable()
class SimpleClass2(x: Int, y: String): SimpleClass(x, y){

}

实例化

fun main() {
    val simpleClass = SimpleClass(9, "Hello")
    println(simpleClass.simpleProperty)
    println(simpleClass.x)
    println(simpleClass.y)
    println(simpleClass.z)
    simpleClass.y()
    simpleClass.zzz("Sleeping ZZZ!")
}

如本文“对您有用”,欢迎随意打赏作者,让我们坚持创作!

0 打赏
Enamiĝu al vi
不要为明天忧虑.因为明天自有明天的忧虑.一天的难处一天当就够了。
543文章 68评论 294点赞 594046浏览

随机文章
Java—并发编程(六)JUC锁 – (8) CountDownLatch
4年前
Redis—StringRedisTemplate和RedisTemplate
5年前
Kotlin-类型进阶—数据类(二十四)
4年前
Redis笔记—连接 Java 客户端(Jedis)
5年前
Kotlin-表达式—分支表达式(十二)
4年前
博客统计
  • 日志总数:543 篇
  • 评论数目:68 条
  • 建站日期:2020-03-06
  • 运行天数:1927 天
  • 标签总数:23 个
  • 最后更新:2024-12-20
Copyright © 2025 网站备案号: 浙ICP备20017730号 身体没有灵魂是死的,信心没有行为也是死的。
主页
页面
  • 归档
  • 摘要
  • 杂图
  • 问题随笔
博主
Enamiĝu al vi
Enamiĝu al vi 管理员
To be, or not to be
543 文章 68 评论 594046 浏览
测试
测试
看板娘
赞赏作者

请通过微信、支付宝 APP 扫一扫

感谢您对作者的支持!

 支付宝 微信支付