Kotlin基础——接口和类

接口

  • 使用 : 表示继承关系,只能继承一个类,但可以实现多个接口
  • 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) : Userclass Man(val email: String) : User {override val name: Stringget() = email.substringBefore("@")
}class WonMan() : User {override val name: String = "A"
}

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

interface User {val email:Stringval name: Stringget() = 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: Booleanget() {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 = falseval currentState: ButtonStateget() = ButtonState(isClickable)fun restoreState(state: ButtonState) {isClickable = state.canClick}class ButtonState(val canClick: Boolean) : Serializableinner 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) : Exprfun 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: Stringinit {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: Stringprivate var age: Int = 0constructor(name: String) {this.name = name}constructor(name: String, age: Int) {this.name = namethis.age = age}
}

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

open class User {private val name: Stringprivate var age: Int = 0constructor(name: String) {this.name = name}constructor(name: String, age: Int) {this.name = namethis.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 = 0private setfun 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: Intget() = innerList.sizeoverride 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: Stringconstructor(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): Tfun 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.hqwc.cn/news/168319.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

iPad系列将在2024年全面更新!

今年还会有新iPad发布吗&#xff1f;答案是否定的。因为早在前几天的季度电话会议上&#xff0c;苹果公司CEO蒂姆・库克就已经宣布&#xff0c;今年不会推出任何新的iPad产品。 这也意味着&#xff0c;今年将是苹果公司自2010年推出首款iPad设备以来&#xff0c;第一次没有发布…

openvino学习(一)ubuntu20.04安装openvino2022

安装openvino2022要求 操作系统 Ubuntu 18.04 长期支持 (LTS)&#xff0c;64 位 Ubuntu 20.04 长期支持 (LTS)&#xff0c;64 位 软件 CMake 3.13 或更高版本&#xff0c;64 位 GCC 7.5.0&#xff08;适用于 Ubuntu 18.04&#xff09;或 GCC 9.3.0&#xff08;适用于 Ubunt…

【Python】二维码和条形码的识别

我主要的问题就在于无法识别图片 注意事项&#xff1a; 1、从文件中加载图像的时候注意图片尽量用英文来命名&#xff0c;因为中文无法识别到图片 2、使用绝对地址的时候要用两个双斜杠&#xff0c;因为用一个会被识别为Unicode 转义&#xff0c;但是并没有后续的合法 Unico…

Linux centos系统中添加磁盘

为了学习与训练文件系统或磁盘的分区、格式化和挂载/卸载&#xff0c;我们需要为虚拟机添加磁盘。根据需要&#xff0c;可以添加多块不同大小的磁盘。具体操作讨论如下&#xff0c;供参考。 一、添加 1.开机前 有两个地方&#xff0c;可选择打开添加硬盘对话框 (1)双击左侧…

最简WebClient 同步、异步调用示例

目录 一&#xff0c;序言二&#xff0c;简单示例1. 引入依赖2. 日志配置3. 调用代码4. 运行结果 三&#xff0c;完整代码 一&#xff0c;序言 WebClient是Spring WebFlux模块提供的一个非阻塞的基于响应式编程的进行Http请求的客户端工具&#xff0c;从Spring5.0开始WebClient…

汽车标定技术(八)--MPC57xx是如何支持标定的页切换

目录 1.页切换的概念 1.1 标定常量的理解 1.2 页切换 2.MPC57xx的Overlay模块 3.小结 1.页切换的概念 在汽车标定测量中&#xff0c;有一个概念我想很多人都听过&#xff0c;但是实际上在项目里没有用到过&#xff0c;那就是今天要讲的页切换概念。在讲页切换的时候&#…

vue分片上传视频并转换为m3u8文件并播放

开发环境&#xff1a; 基于若依开源框架的前后端分离版本的实践&#xff0c;后端java的springboot&#xff0c;前端若依的vue2&#xff0c;做一个分片上传视频并分段播放的功能&#xff0c;因为是小项目&#xff0c;并没有专门准备文件服务器和CDN服务&#xff0c;后端也是套用…

Shopee的折扣活动怎么分类?shopee设置折扣注意事项

旺季到来&#xff0c;Shopee会举办一些折扣活动来吸引客户&#xff0c;那么shopee的折扣活动怎么分类&#xff0c;shopee设置折扣注意事项&#xff1f; shopee的折扣活动怎么分类&#xff1f; 满减活动&#xff1a;满减活动是虾皮常见的一种折扣形式。在这种活动中&#xff0…

JavaScript使用Ajax

Ajax(Asynchronous JavaScript and XML)是使用JavaScript脚本&#xff0c;借助XMLHttpRequest插件&#xff0c;在客户端与服务器端之间实现异步通信的一种方法。2005年2月&#xff0c;Ajax第一次正式出现&#xff0c;从此以后Ajax成为JavaScript发起HTTP异步请求的代名词。2006…

初探SVG

SVG&#xff0c;可缩放矢量图形&#xff08;Scalable Vector Graphics&#xff09;。使用XML格式定义图像。SVG有以下优点&#xff1a;1&#xff09;可被非常多的工具读取和修改&#xff1b;2&#xff09;比JPEG和GIF尺寸更小&#xff0c;可压缩性更强&#xff1b;3&#xff09…

C++——基础

初学C的时候&#xff0c;有没有想过&#xff0c;为什么C支持重载&#xff0c;而C不支持重载呢&#xff1f;&#xff1f; 其实&#xff0c;一个程序运行起来都要经过四步骤 预处理编译汇编链接 预处理阶段会经过去注释&#xff0c;宏替换&#xff0c;头文件展开&#xff0c;条…

Java Web——HTTP协议

目录 1. HTTP协议概述 1.1. HTTP数据传输格式 1.2. HTTP协议特点 2. HTTP 1.0和HTTP 1.1 3. HTTP请求协议 3.1. GET方式请求协议 3.2. POST方式请求协议 3.3. GET请求和POST请求的区别 4. HTTP相应协议 4.1. 响应状态码 如果两个国家进行会晤需要遵守一定的礼节。所以…