前言
在上一篇中,讲解了Kotlin关于对象的基础知识,在这一篇中,将会讲解Kotlin对应的对象,接口,抽象类相关的知识点。
1. 类的单例
1.1 示例一
object ApplicationConfig {
init {
println("loading config ...")
}
fun setSomeThing() {
println("setSomething")
}
}
fun main{
ApplicationConfig.setSomeThing()
println(ApplicationConfig)
println(ApplicationConfig)
ApplicationConfig.setSomeThing()
}
我们这里看到,对象ApplicationConfig 含有init 初始化代码块,在main函数入口这,多次调用了ApplicationConfig 对象。现在来看看运行效果:
loading config ...
setSomething
ApplicationConfig@63e31ee
ApplicationConfig@63e31ee
setSomething
从这个运行效果可知,当使用object 修饰对象时,当前对象只能产生一个实例的类-单例!
既然如此,那如果说使用object 修饰的对象继承了某个父类会怎样呢?
1.2 示例二
open class Player {
open fun load() = "loading nothing"
open fun add(one: Int, two: Int): Int {
return one + two
}
}
fun main{
val p = object : Player() {
override fun load() = "anonymous class load..."
override fun add(one: Int, two: Int): Int {
return one-two
}
}
println(p.load())
println(p.add(10, 20))
}
这里我们看到,定义了object 对象继承了Player ,并且重写了对应父类的方法,将原有逻辑改成了自己的。来看看运行效果:
anonymous class load...
-10
用这种方式,它的生命周期非常短,属于随用随丢的那种。
2. 类的嵌套
class Player2() {
class Equipment(val name: String) {
fun show() = println("equipment: $name")
}
fun battle() {
Equipment("sharp knife").show()
}
companion object {
fun staticBattle() {
Equipment("staticBattle knife").show()
}
}
}
fun main{
Player2.Equipment("AK47").show()
var p = Player2();
p.battle();
Player2.staticBattle()
}
这个使用了嵌套类,主要介绍的事,对象里面的方法有哪些方式能够访问。
普通方法battle ,需要实例化Player2 对象后才能访问;通过companion object 闭包里的方法,可以直接通过类名.方法名 的方式直接访问(类似java的static)
来看看运行效果:
equipment: AK47
equipment: sharp knife
equipment: staticBattle knife
这些都狠简单,直接过!
3. 数据类
数据类?那它有啥作用?
- 数据类,是专门设计用来存储数据的类
- 数据类提供了 toString的个性化实现
- 符号默认情况下,比较对象就是比较他们的属性值,数据类提供了equals和hashCode的个性化实现
- data 重写了 equals 和 hashCode 的实现
说了那么多!那它有啥使用条件?
- 数据类必须有至少带一个参数的主构造函数
- 数据类主构造函数的参数必须是val或var
- 数据类不能使用abstract、open、sealed和inner修饰符
概念说完了,实际体验一下。
3.1 示例一
data class Coordinate(var x: Int, var y: Int) {
val isInBounds = x >= 0 && y >= 0
}
fun main{
println(Coordinate(1, 5))
println(Coordinate(1, 5))
println(Coordinate(1, 5) == Coordinate(1, 5))
println(Coordinate(1, 5) === Coordinate(1, 5))
}
这里看到使用也挺简单的,在main里面定义了两个值相同的Coordinate 数据类,然后将它们的值和地址分别进行对等判断,来看看运行效果:
Coordinate(x=1, y=5)
Coordinate(x=1, y=5)
true
false
从这个运行效果可以看出,每次 创建的数据类,即使值相同,赋的初始值也相同,它们的地址都不相同。也就是说,每次创建数据类,它将开辟一个新的地址供自己使用。
3.2 示例二
data class Student(var name: String, var age: Int, val sex: Int) {
var score = 10
private val hobby = "music"
var subject: String
init {
println("initializing student")
subject = "math"
}
constructor(_name: String, _age: Int) : this(_name, _age, 1) {
score = 40
}
constructor(_name: String) : this(_name, 10, 2) {
score = 20
subject = ""
}
override fun toString(): String {
return "Student(name='$name', age=$age, sex=$sex, score=$score, hobby='$hobby', subject='$subject')"
}
}
fun main{
val s = Student("jack")
println(s)
var copy = s.copy("tom")
println(copy)
copy = s.copy("bob",30)
println(copy)
}
这里使用了copy 函数,将对象S的数据copy了一份。
注意看,代码里面除了主构造函数外,还额外定义了两个次构造函数。
先看运行效果:
initializing student
Student(name='jack', age=10, sex=2, score=20, hobby='music', subject='')
initializing student
Student(name='tom', age=10, sex=2, score=10, hobby='music', subject='math')
initializing student
Student(name='bob', age=30, sex=2, score=10, hobby='music', subject='math')
从这个运行效果可以看出:当使用copy复制对象时,将会copy里面的值将会带入主构造函数里,并非对应的次构造函数!
3.3 示例三
data class Coordinate2(var x: Int, var y: Int) {
val isInBounds = x >= 0 && y >= 0
operator fun plus(other: Coordinate2) = Coordinate2(x + other.x, y + other.y)
}
fun main{
val c1 = Coordinate2(5, 6)
val c2 = Coordinate2(10, 20)
println(c1 + c2)
}
运行效果:
Coordinate2(x=15, y=26)
注意看,这里重载plus 对应的操作符,于是乎Coordinate2 具有加法运算。当使用数字符号+ 的时候就会调用对应plus 方法。
4. 解构声明
在开始之前,要先明白何为解构?
还记得之前讲解过,可同时定义多个变量拆分集合/数组里面的每个元素么?
fun main
val(a,b,c)= listOf("json", "jack", "jacky")
println(a)
println(b)
println(c)
}
比如这样,定义a、b、c 分别拆分赋值集合里面的元素,这就是解构。当然这个是SDK帮我们封装好了。
那如果说,我们想要弄出这个格式该怎么办呢?
class PlayerScore(val experience: Int, val level: Int) {
operator fun component1() = experience
operator fun component2() = level
}
fun main{
val (x,y)=PlayerScore(10,4)
println(x)
println(y)
}
这里我们看出:
- 在Class对象里通过
operator fun component1() 指定了第一个变量的值和类型, - 通过
operator fun component2() 指定了第二个变量的值和类型 - 同理
operator fun component数字N() 指定了第N个变量的值和类型
注意:这里一定要按照顺序依次定义!
5. 枚举
5.1 示例一
enum class Direction {
EAST,
WEST,
SOUTH,
NORTH
}
fun main{
println(Direction.EAST)
}
运行效果
EAST
这简单的不能再简单了,直接过!
5.2 示例二
data class Coordinate(var x: Int, var y: Int) {
}
enum class Direction2(private val coordinate: Coordinate) {
EAST(Coordinate(5, -1)),
WEST(Coordinate(1, 0)),
SOUTH(Coordinate(0, 1)),
NORTH(Coordinate(-1, 0));
fun updateCoordinate(playerCoordinate: Coordinate): Coordinate {
return Coordinate(playerCoordinate.x + coordinate.x, playerCoordinate.y + coordinate.y)
}
}
fun main{
println(Direction2.EAST.updateCoordinate(Coordinate(10,20)))
}
运行效果
Coordinate(x=15, y=19)
这也不怎么复杂,就是指定EAST 枚举,将对应的x,y值与Coordinate里面的x,y值进行逻辑运算,最后再打印Coordinate 对象。
5.3 示例三
enum class LicenseStatus {
UNQUALIFIED,
LEARNING,
QUALIFIED;
}
class Driver(var status: LicenseStatus) {
fun checkLicense(): String {
return when (status) {
LicenseStatus.LEARNING -> "在学习"
LicenseStatus.QUALIFIED -> "有资格已毕业"
LicenseStatus.UNQUALIFIED -> "没有资格"
}
}
}
fun main{
var driver = Driver(LicenseStatus.QUALIFIED)
println(driver.checkLicense())
}
运行效果
有资格已毕业
这个很简单,只要看了之前的语法,这应该能够一眼看出其意思!
但结合例子,思考一个场景:正常人在毕业后一般都会有个毕业编号啥的。但如果单靠枚举的话,只能确定这个人毕业了,并不能在确认的同时将毕业编号显示出来!但如果想要那种效果怎么办呢?
5.4 示例四(密封类)
sealed class LicenseStatus2 {
object UnQualified : LicenseStatus2()
object Learning : LicenseStatus2()
class Qualified(val licenseId: String) : LicenseStatus2()
}
class Driver2(var status: LicenseStatus2) {
fun checkLicense(): String {
return when (status) {
is LicenseStatus2.UnQualified -> "没有资格"
is LicenseStatus2.Learning -> "在学习"
is LicenseStatus2.Qualified -> "有资格,驾驶证编号:" +
"${(this.status as LicenseStatus2.Qualified).licenseId}"
}
}
}
fun main{
var driver=Driver2(LicenseStatus2.Qualified("ds5464564"))
println(driver.checkLicense())
}
注意这次并不是用的enum 了,而是使用的sealed class ,让它变成了密封类。当对应选项不需要参数时,可以使用object 定义;当对应选项需要对应的属性值时,可以用class 通过主构造函数将参数传入进去。
6. 接口
interface Moveable {
val maxSpeed: Int
var wheels: Int
fun move(moveable: Moveable): String
}
class Car(_name: String, override var wheels: Int = 4) : Moveable {
override val maxSpeed: Int
get() = TODO("Not yet implemented")
override fun move(moveable: Moveable): String {
TODO("Not yet implemented")
}
}
接口也挺简单的,和java几乎一致,就不过多演示了!
结束语
好了,本篇到这里就结束了!到现在为止,类的相关知识点已经讲解完毕!从下一篇开始将会讲解Kotlin对应的扩展以及函数式编程!
|