Kotlin基础——接口和类

news2024/12/27 12:23:52

接口

  • 使用 : 表示继承关系,只能继承一个类,但可以实现多个接口
  • override修饰符表示重写
  • 可以有默认方法,若父类的默认方法冲突,则需要子类重写,使用super<XXX>.xxx()调用某一父类方法
interface Focusable {
    fun focus()
    fun show() = println("Focusable")
}

class Button : Clickable, Focusable {
    override fun click() {
        TODO("Not yet implemented")
    }

    override fun focus() {
        TODO("Not yet implemented")
    }

    override fun show() {
        super<Clickable>.show()
        super<Focusable>.show()
    }
}

接口中可以声明域,每个子类都要初始化接口中的域

interface User {
    val name: String
}

class Person(override val name: String) : User

class Man(val email: String) : User {
    override val name: String
        get() = email.substringBefore("@")
}

class WonMan() : User {
    override val name: String = "A"
}

接口中也可以使用getter和setter,前提是不引用变量

interface User {
    val email:String
    val name: String
    get() = email.substringBefore('@')
}

bean类

只有数据没有其他代码的对象通常叫做值对象,如JavaBean

public class Person {
    private String name;

    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
}

使用过程如下

Person person = new Person("java", 8);

person.setName("kotlin");
System.out.println(person.getName());
System.out.println(person.getAge());

将上述Java复制到代码.kt文件,会触发自动转换(.kt不要求类名和文件名一致,可将多个类放到同一文件,且文件名随意

在这里插入图片描述

转换后的代码如下,name为var变量(默认带有getter和setter),而age为val变量(只有getter)

class Person(
    var name: String,
    val age: Int
) 

使用方法如下

val person = Person("java", 8);

person.name = "kotlin"
println(person.name)
println(person.age)

如果一个属性可以根据其他属性计算,可使用自定义getter

class Rectangle(val height: Int, val width: Int) {
    val isSquare: Boolean
        get() {
            return height == width
        }
}

类的重写

类和方法默认都是final的,否则需要使用open修饰符

open class Button {
    fun click() {}
    open fun focus() {}
}

open class MyButton : Button() {
    override fun focus() {
        super.focus()
    }
}

抽象类

抽象方法默认为open,其他的可有可无

abstract class Button {
    abstract fun click()
    open fun focus() {}
    fun press() {}
}

open class MyButton : Button() {
    override fun click() {
        TODO("Not yet implemented")
    }
}

可见性

Kotlin没有包的概念,internal为模块可见性

在这里插入图片描述

  • private类在Java中会被编译成protect
  • internal类或域在Java中会被编译成public

嵌套类

在一个类中声明另一个类

  • 在Java中,未用static声明的类为内部类(含有外部类的隐式引用),加上static的类为嵌套类(不含有外部类的隐式引用)
  • 而Kotlin相反,未用inner声明的类为嵌套类(不含有外部类的隐式引用),加上inner的类为内部类(含有外部类的隐式引用)
  • 嵌套类不能访问外部类的实例,外部类不能访问嵌套类中的private域

在这里插入图片描述

如果需要使用内部类存储序列化信息,需要声明为static转为嵌套类,否则将无法序列化

public class Button {

    private boolean isClickable;

    public ButtonState getCurrentState() {
        return new ButtonState(this.isClickable);
    }

    public void restoreState(ButtonState state) {
        this.isClickable = state.canClick;
    }

    public static class ButtonState implements Serializable {
        private boolean canClick;

        public ButtonState(boolean isClickable) {
            this.canClick = isClickable;
        }
    }
}

而在Kotlin中正好相反,默认为加上static的嵌套类,如果需要成为内部类则应加上inner修饰符,使用this@Outer访问外部类实例

class Button {
    private var isClickable = false

    val currentState: ButtonState
        get() = ButtonState(isClickable)

    fun restoreState(state: ButtonState) {
        isClickable = state.canClick
    }

    class ButtonState(val canClick: Boolean) : Serializable

    inner class TestState(val canClick: Boolean) {
        fun getOuterReference(): Button = this@Button
    }
}

受限的类继承结构

在如下结构和判断中,若新增了类,但却没有新增分支,会导致其调用走到else

interface Expr
class Num(val value: Int) : Expr
class Sum(val left: Expr, val right: Expr) : Expr

fun eval(e: Expr): Int =
    when (e) {
        is Num -> {
            e.value
        }
        is Sum -> {
            eval(e.left) + eval(e.right)
        }
        else -> {
            throw IllegalArgumentException("")
        }
    }

通过sealed修饰父类,将子类嵌套在父类,可避免额外的else分支,当新增子类时,when表达式会提示编译失败

sealed class Expr {
    class Num(val value: Int) : Expr()
    class Sum(val left: Expr, val right: Expr) : Expr()
}

fun eval(e: Expr): Int =
    when (e) {
        is Expr.Num -> {
            e.value
        }
        is Expr.Sum -> {
            eval(e.left) + eval(e.right)
        }
    }

主构造函数

class+类名+[修饰符]+constructor()表示主构造函数,init表示初始化语句块

class User private constructor(name: String) {
    val name: String

    init {
        this.name = name
    }
}

若主构造函数没有注解或可见性修饰符,上述还可以省略为

class User(name: String) {
    val name: String = name
}

如果参数用相应的构造方法参数来初始化,还可以简化为

class User(val name: String) 

也可以加上默认参数,若所有构造方法参数都有默认值,会生成一个额外的不带参数的构造方法来使用所有的默认值

class User(val name: String = "A")

若类有父类,可在继承列表中调用父类构造函数初始化父类,如下传入Person的参数name会被用于构造User

open class User(val name: String)

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

}

从构造函数

当需要多个构造函数时,可使用从构造函数

open class User {
    private val name: String
    private var age: Int = 0

    constructor(name: String) {
        this.name = name
    }

    constructor(name: String, age: Int) {
        this.name = name
        this.age = age
    }
}

可使用this和super调用自身及父类的构造从构造函数,若类没有主构造函数,那么每个从构造函数必须初始化父类

open class User {
    private val name: String
    private var age: Int = 0

    constructor(name: String) {
        this.name = name
    }

    constructor(name: String, age: Int) {
        this.name = name
        this.age = age
    }
}

class Person : User {
    constructor(name: String) : this(name, 18) {
    }

    constructor(name: String, age: Int) : super(name, age) {

    }
}

通过setter/getter访问域的值

在setter中可以通过标识符field读写域的值,getter中只能读

class User(val name: String) {
    var address: String = "unspecified"
        set(value: String) {
            println(
                """
                |Address was changed for $name:
                |"$field" -> "$name"
            """.trimMargin()
            )
            field = value
        }
}

如上,在更新address值时

val user = User("Tom")
user.address = "A Street"

额外打印一些信息

Address was changed for Tom:
"unspecified" -> "Tom"

修改getter/setter可见性

如下,使用内部计数字符个数,不能对其setter

class lengthCounter {
    var counter: Int = 0
        private set

    fun addWord(word: String) {
        counter += word.length
    }
}

data类

data修饰符将会为类自动生成通用方法的实现,如toString()、equals()和hashCode(),会将主构造函数中申明的属性纳入考虑

data class Person(val name: String, val age: Int)

Kotlin还为data类新增了copy(),用于返回相同的对象,且互不影响

val A = Person("A", 18)
val B = A.copy();
println(A)
println(B)

通过by实现类委托

在新增对象功能时,原有功能通常会被委托给原对象,这样会出现大量样板代码

class MyCollection<T> : Collection<T> {
    private val innerList = arrayListOf<T>()
    override val size: Int
        get() = innerList.size

    override fun contains(element: T): Boolean {
        return innerList.contains(element)
    }

    override fun containsAll(elements: Collection<T>): Boolean {
        return innerList.containsAll(elements)
    }

    override fun isEmpty(): Boolean {
        return innerList.isEmpty()
    }

    override fun iterator(): Iterator<T> {
        return innerList.iterator()
    }
}

可通过 by 将接口的实现委托到另一个对象,编译器会自动实现上面的方法,也可自行重写方法

class MyCollection<T>(innerList: Collection<T> = ArrayList<T>()) : Collection<T> by innerList {

}

object关键字

定义一个类并同上创建一个实例

使用对象声明创建单例

一个对象声明不允许有构造方法,在定义时即被创建,不需要再调用构造方法

object Person {
    val name: String = ""
    fun printName() {

    }
}

可使用对象.属性/方法来访问

Person.name = "A"
Person.printName()

当实现一个接口但不包含任何状态时,通常使用对象声明,如实现一个比较器

object CaseInsensitiveFileComparator : Comparator<File> {
    override fun compare(p0: File, p1: File): Int {
        return p0.path.compareTo(p1.path, true)
    }
}

在使用时可直接传递到接收Comparator的函数

println(CaseInsensitiveFileComparator.compare(File("/User"), File("/user")))

val files = listOf(File("/Z"), File("/a"))
println(files.sortedWith(CaseInsensitiveFileComparator))

在Java中被编译成通过静态字段持有单例,则通过如下调用

CaseInsensitiveFileComparator.INSTANCE.compare(File("/User"), File("/user"));

伴生对象代替静态方法和字段

创建工厂方法

class User {
    val name: String

    constructor(email: String) {
        name = email.substringBefore('@')
    }

    constructor(id: Int) {
        name = id.toString()
    }
}

使用companion定义伴生对象,将上面改成工厂方法实现

class User private constructor(val name: String) {
    companion object {
        fun newUserByEmail(email: String) = User(email.substringBefore('@'))
        fun newUserById(id: Int) = User(id.toString())
    }
}

在使用时,可通过类名称访问对象的方法和属性

val user1 = User.newUserByEmail("xxx@gmail.com")
val user2 = User.newUserById(123)

伴生对象在Java中通过如下方式调用,若存在名字,则代替Companion

User.Companion.newUserById(123)

实现接口

伴生对象也可实现接口

interface Factory<T> {
    fun newUserByEmail(email: String): T
    fun newUserById(id: Int): T
}

class User(val name: String) {
    companion object : Factory<User> {
        override fun newUserByEmail(email: String) = User(email.substringBefore('@'))
        override fun newUserById(id: Int) = User(id.toString())
    }
}

对于如下方法

fun <T> createUser(factory: Factory<T>) {

}

可直接将伴生对象所在类名字当作实现了该接口的对象实例来使用

createUser(User)

扩展函数

当需要定义通过类调用的方法时,可以通过伴生对象函数来实现

class User(val name: String) {
    companion object {

    }
}

fun User.Companion.printName() {
    
}

使用如下

User.printName()

对象表达式创建匿名对象

object声明匿名对象代替Java的匿名内部类

interface OnClickListener {
    fun onclick()
}

fun setOnClickListener(listener: OnClickListener) {

}

对于上面常用的监听事件,可传入声明匿名对象

setOnClickListener(object : OnClickListener {
    override fun onclick() {
    
    }
})

若需要命名则提取出来,匿名对象可以实现多个接口

val listener = object : OnClickListener {
    override fun onclick() {
    }
}
setOnClickListener(listener)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1199507.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

免费录屏软件哪个好用?免费录屏软件排行榜

对于您的团队&#xff0c;屏幕录像机可以用于多种原因——从为您的网站创建教程到记录反复出现的技术问题&#xff0c;再到向您的营销团队发送快速说明而不是电子邮件。 此外&#xff0c;我们不能忘记产品演示和培训视频&#xff0c;它们可供您团队中的许多部门使用&#xff0…

matlab模糊控制文件m代码实现和基础理论

1、内容简介 略 15-可以交流、咨询、答疑 通过m代码来实现生成模糊文件fis文件 2、内容说明 模糊文件m代码实现和基础理论 matlab模糊控制文件m代码实现和基础理论 模糊文件、m代码和模糊基础理论 3、仿真分析 略 4、参考论文 略 链接&#xff1a;https://pan.baidu.co…

web前端开发第3次Dreamweave课堂练习/html练习代码《网页设计语言基础练习案例》

目标图片&#xff1a; 文字素材&#xff1a; 网页设计语言基础练习案例 ——几个从语义上和文字相关的标签 * h标签&#xff08;h1~h6&#xff09;&#xff1a;用来定义网页的标题&#xff0c;成对出现。 * p标签&#xff1a;用来设置网页的段落&#xff0c;成对出现。 * b…

NZ系列工具NZ08:图表添加标签工具

我的教程一共九套及VBA汉英手册一部&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到数据库&#xff0c;到字典&#xff0c;到高级的网抓及类的应用。大家在学习的过程中可能会存在困惑&#xff0c;这么多知识点该如何组织…

【车载开发系列】AutoSar中的CANTP

【车载开发系列】AutoSar中的CANTP 【车载开发系列】AutoSar中的CANTP 【车载开发系列】AutoSar中的CANTP一. CANTP相关术语二. CANTP相关概念1&#xff09;单帧&#xff1a;SF(Single Frame)2&#xff09;首帧&#xff1a;FF(First Frame)3&#xff09;连续帧CF(Consecutive F…

腾讯云3年轻量2核4G5M服务器756元,抓紧数量不多

腾讯云轻量应用服务器特价是有新用户限制的&#xff0c;所以阿腾云建议大家选择3年期轻量应用服务器&#xff0c;一劳永逸&#xff0c;免去续费困扰。腾讯云轻量应用服务器3年可以选择2核2G4M和2核4G5M带宽&#xff0c;3年轻量2核2G4M服务器540元&#xff0c;2核4G5M轻量应用服…

十五、信号量

1、概述 (1)前面介绍的队列(queue)可以用于传输数据&#xff1a;在任务之间、任务和中断之间。 (2)有些时候我们只需要传递状态&#xff0c;并不需要传递具体的信息&#xff0c;比如&#xff1a; 我的事做完了&#xff0c;通知一下你。卖包子了、卖包子了&#xff0c;做好了…

python 中用opencv开发虚拟键盘------可以只选择一个单词不会出现一下选择多个

一. 介绍 OpenCV是最流行的计算机视觉任务库&#xff0c;它是用于机器学习、图像处理等的跨平台开源库&#xff0c;用于开发实时计算机视觉应用程序。 CVzone 是一个计算机视觉包&#xff0c;它使用 OpenCV 和 Media Pipe 库作为其核心&#xff0c;使我们易于运行&#xff0c…

基于STM32控制直流电机加减速正反转仿真设计

**单片机设计介绍&#xff0c;基于STM32控制直流电机加减速正反转仿真设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 本设计由STM32F103、L298N电机驱动电路、按键电路组成。通过按键可以控制电机&#xff0c;正转、反转、加…

【FISCO BCOS】十九、区块链浏览器部署

目录 一、环境依赖 检查环境 1.检查java 二、拉取安装脚本 获取部署安装包 ​编辑 解压安装包 进入目录 三、修改配置 四、部署服务 五、状态检查 检查前后端进程 1.检查后端server进程 2.检查前端的nginx进程 检查进程端口 六、使用区块链浏览器 1.配置群组…

关于DataLoader是否shuffle在VOC2007语义分割数据集上引发的问题

问题描述&#xff1a; 在训练过程中&#xff0c;训练集和验证集实时得到的F1分数相差很大&#xff0c;如下图&#xff1a; 这个问题之前从未遇到过&#xff0c;后来经过不断的排查&#xff0c;发现是因为验证集的数据加载器中shuffle设置的为False&#xff0c;而训练集设置的为…

[C/C++]数据结构 深入挖掘环形链表问题

前言 在上一篇文章中讲述了如何判断链表是否带环,在观看本片文章时建议先了解一下这篇文章的内容[C/C]数据结构 链表OJ题:环形链表。本篇文章我们将讲述关于环形链表的几种不同的情况如下,同时我们要解决另一个环形链表问题----找到入环点 slow一次走一步fast一次走两步一定会…

进亦忧,退亦忧,Github Copilot 集成进入 Visual Studio 带来的思考

开篇想到《岳阳楼记》的结尾&#xff1a; 不以物喜&#xff0c;不以己悲&#xff1b;居庙堂之高则忧其民&#xff1b;处江湖之远则忧其君。是进亦忧&#xff0c;退亦忧。然则何时而乐耶&#xff1f;其必曰&#xff1a;“先天下之忧而忧&#xff0c;后天下之乐而乐”乎。未来30…

第26章_事务概述与隔离级别

文章目录 事务事务的特征事务的控制语句事务的生命周期事务的执行过程 ACID特性原子性一致性隔离性持久性 隔离级别不同隔离级别并发异常脏读不可重复读幻读区别 总结 事务 &#xff08;1&#xff09;事务的前提&#xff1a;并发连接访问。MySQL的事务就是将多条SQL语句作为整…

SpringDataJpa(二)

三、Spring Data JPA概述 Spring Data JPA 是 Spring 基于 ORM 框架、JPA 规范的基础上封装的一套JPA应用框架&#xff0c;可使开发者用极简的代码即可实现对数据库的访问和操作。它提供了包括增删改查等在内的常用功能&#xff0c;且易于扩展&#xff01;学习并使用 Spring D…

汽车操纵稳定性matlab仿真

1、内容简介 略 14-可以交流、咨询、答疑 2、内容说明 汽车操纵稳定性matlab仿真&#xff0c;包含完整的论文 操纵动力学、两自由度 摘要&#xff1a;当今&#xff0c;仿真技术日益广泛地应用于汽车工程领域&#xff0c;操纵稳定性研究越来越多地使用成熟的计算机仿真理论…

【Proteus仿真】【STM32单片机】多路温度控制系统

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器&#xff0c;使用按键、LED、蜂鸣器、LCD1602、DS18B20温度传感器、HC05蓝牙模块等。 主要功能&#xff1a; 系统运行后&#xff0c;默认LCD1602显示前4路采集的…

Spring学习笔记——AOP(4)

Spring学习笔记——AOP&#xff08;4&#xff09; 一、学习AOP1.1 AOP的概述1.2 AOP思想实现方案1.3、模拟AOP的基础代码1.4、AOP的相关概念 二、基于xml配置AOP2.1 AOP基础入门2.2、XML方式AOP配置详解2.3、XML方式AOP原理剖析 三、注解式开发AOP3.1 注解式开发AOP入门3.2 AOP…

Swift 常用类别整理

生成颜色&#xff0c;传入16进制数字生成对应颜色 个人不喜欢传字符串的写法&#xff0c;比如 "0x0080FF" 或者 "0080FF"&#xff0c;原因如下&#xff1a; 传了字符串最后还是要解析成数字参与颜色运算的&#xff0c;需要额外做字符串转数字的操作&…

【C++】C++入门详解 I【C++入门 这一篇文章就够了】

C入门 前言一、C关键字&#xff08;C98&#xff09;二、命名空间 namespace&#xff08;一&#xff09;namespace的出现&#xff08;二&#xff09;namespace的定义&#xff08;1&#xff09;namespace 的正常定义&#xff08;2&#xff09;namespace的功能特性1. 命名空间 可嵌…