目录
在这里感谢Jason老师。我是他学生。
1.对象
1.1.嵌套类
如果一个类只对另一个有用,那么将其嵌套到该类中,并使这两个类保持在一起是符合逻辑的,可使用嵌套类。
//嵌套类
class Player2() {
class Equipment(var equipName:String) {
fun showEquip() {
println("Equipment: $equipName")
}
}
fun battle() {
Equipment("knife").showEquip()
}
}
fun qiantaoTest() {
Player2.Equipment("比尔特弯刀").showEquip()
Player2().battle()
}
运行结果:Equipment: 比尔特弯刀
Equipment: knife
可以看到这两种用法都可以。
1.2.数据类
1.数据类使专门用来设计存储数据的。2.提供了toString、equals和hashCode的个性化实现。3.==符号默认情况使比较它们的引用值。
//数据类
data class Coordinate(var x:Int, var y:Int) {
}
fun dataTest() {
println(Coordinate(10, 100))
}
运行结果:Coordinate(x=10, y=100)
data默认实现了toString函数,所以输出的会与Any不同。
4.数据类还提供了一个函数,可以方便的复制一个对象,copy还可以选择的修改其中要修改的值。
//copy 坑点:copy的时候走的是主函数,如果有些属性在次构造有调整,那么就会出现不同
data class Student(val name:String, var age:Int) {
var score = 56;
var subject = "math"
override fun toString(): String {
return "Student(name='$name', age=$age, score=$score, subject='$subject')"
}
}
fun copyTest() {
var s = Student("huahua", 24);
println(s)
println(s.copy(name = "jack"))
}
运行结果:Student(name='huahua', age=24, score=56, subject='math')
Student(name='jack', age=24, score=56, subject='math')
其中的坑点是:如果你要copy一个次构造函数实例的对象,并且有些属性在次构造里修改。那么copy出来的值将会是不同的。详细可以见字节码,大家可以试试。
5.支持解构。
//解构
class PlayerScore(var score:Int, var level:Int) {
operator fun component1() = score;
operator fun component2() = level;
}
fun jiegouTest() {
val (x1, x2) = PlayerScore(56, 2)
println("x1 = ${x1}; x2 = $x2")
}
@Metadata(
mv = {1, 5, 1},
k = 1,
d1 = {"..."},
d2 = {...}
)
public static final class PlayerScore {
private int score;
private int level;
public final int component1() {
return this.score;
}
public final int component2() {
return this.level;
}
public final int getScore() {
return this.score;
}
public final void setScore(int var1) {
this.score = var1;
}
public final int getLevel() {
return this.level;
}
public final void setLevel(int var1) {
this.level = var1;
}
public PlayerScore(int score, int level) {
this.score = score;
this.level = level;
}
}
运行结果: x1 = 56; x2 = 2
这不是数据类,没有被data修饰。这是普通类的解构方式,看字节码我们可以看出解构的有用component修饰,数据类默认是给我们提供了结构的。详情看字节码。
public static final class Student {
private int score;
@NotNull
private String subject;
@NotNull
private final String name;
private int age;
........
@NotNull
public String toString() {
return "Student(name='" + this.name + "', age=" + this.age + ", score=" + this.score + ", subject='" + this.subject + "')";
}
@NotNull
public final String getName() {
return this.name;
}
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
public Student(@NotNull String name, int age) {
......
}
@NotNull
public final String component1() {
return this.name;
}
public final int component2() {
return this.age;
}
@NotNull
public final ObjectTest.Student copy(@NotNull String name, int age) {
.....
}
// $FF: synthetic method
public static ObjectTest.Student copy$default(ObjectTest.Student var0, String var1, int var2, int var3, Object var4) {
......
}
public int hashCode() {
......
}
public boolean equals(@Nullable Object var1) {
....
}
}
这是上边Student的字节码,看到这个就证明了刚才所述。1.数据类输出不一样是重载了toString;2.我们也可以重载equals和hashCode来实现自己的需求;3.并且有解构component;4.copy方法。
数据类注意项:
1.必须有至少带有一个参数的主构造函数
2.主构造函数里的参数不能为临时变量,一定要var/val 修饰的变量
3.不能使用abstract、open、sealed和inner修饰。
1.3.枚举类
//枚举类
enum class Direct {
EAST,
WEST,
SOUTH,
NORTH
}
fun enumTest() {
println(Direct.NORTH)
}
运行结果:NORTH
枚举类也能定义函数和自定义函数
//数据类
data class Coordinate(var x:Int, var y:Int) {
}
//枚举类有主构造函数 和 自定义的函数
enum class Direction(private var coordinate: Coordinate) {
EAST(Coordinate(1, 1)),
WEST(Coordinate(2, 2)),
SOUTH(Coordinate(3, 3)),
NORTH(Coordinate(4, 4));
fun updateCoordinate(cdinate:Coordinate) : Coordinate {
return Coordinate(coordinate.x + cdinate.x, coordinate.y + cdinate.y)
}
}
fun enumTest2() {
println(Direction.EAST.updateCoordinate(Coordinate(20, 20)))
}
运行结果:Coordinate(x=21, y=21)
其中可以能不太理解EAST、WEST、SOUTH和NORTH为啥这么写,你可以理解EAST...都是Direction的实例,然后我们给枚举类定义了主构造函数,所以要传参。
1.4.运算符重载
如果要将内置运算符应用在自定义类上,你必须重写运算符函数,告诉编译器该如何操作自定义类。
//运算符重载
data class Coordinate2(var x:Int, var y:Int) {
operator fun plus(other:Coordinate2):Coordinate2 {
return Coordinate2(x + other.x, y + other.y)
}
}
fun dataTest2() {
val coordinate = Coordinate2(1, 2)
val coordinate2 = Coordinate2(2, 3)
println(coordinate + coordinate2)
}
运行结果:Coordinate2(x=3, y=5)
这边我重载的方法是 +。其他的大家可以自行试试。重载的时候记得加operate修饰
操作符所对应的函数名,大家可去官网自行查阅。我这边写几个对应的:
操作符 | 函数名 |
+ | plus |
+= | plusAssign |
== | equals |
> | compareTo |
[] | get |
.. | rangeTo |
in | contains |
1.5.密封类
//密封类
sealed class LicenseStatus {
object UnQualified : LicenseStatus()
object Learning : LicenseStatus()
class Qualified(val licenseId:String) : LicenseStatus()
}
class Police(private val licenseStatus: LicenseStatus) {
fun checkLicense() : String {
return when(licenseStatus) {
is LicenseStatus.UnQualified -> "没资格"
is LicenseStatus.Learning -> "在学"
is LicenseStatus.Qualified -> "有资格,驾驶证编号:${(this.licenseStatus as LicenseStatus.Qualified).licenseId}"
}
}
}
fun sealedTest() {
println(Police(LicenseStatus.Qualified("356431223")).checkLicense())
}
运行结果:有资格,驾驶证编号:356431223
讲下这个代码的需求场景:警察查驾照,驾照我给的状态是3个,没有、在学和有驾照。其中有驾照就有驾照号,我需要输出查阅驾照号。如果用枚举类,再写一个变量var licenseId:String 那么其他两个状态都有这个变量,都可以对这么变量进行操作,但我只要有驾照才有这个变量,用密封类就比较快,3个类,object修饰类是有单例的效果,之前的学习笔记有讲到。没有驾照和在学习中没有任何属性单例也不太有所谓,有驾照的就class声明一个,但他们都继承自LicenseStatus。然后在做判断的时候要用is判断它具体是什么类。当然我讲的这个需求肯定有比这还好的方法,只是我觉得这样需求比较好描述密封类。
2.接口
kotlin规定所有的接口属性和函数实现都要使用override关键字,接口中定义的函数不需要open修饰,默认就是open的。
interface EventListener {
fun clickListenerCallBack(listener: EventListener)
}
class Event(_name:String) : EventListener{
override fun clickListenerCallBack(listener: EventListener) {
TODO("Not yet implemented")
}
}
字节码中没有被final定义,所以咱们说默认是open的。
public interface EventListener {
void clickListenerCallBack(@NotNull IntefaceTest.EventListener var1);
}
3.抽象类
abstract class Car(val name:String, val speed:Int) {
abstract fun accelerate() : String
}
class Motorcycle() : Car("motorcycle", 100) {
override fun accelerate(): String {
return "我的速度是$speed,我的名字是$name"
}
}
fun AbstractTest1() {
println(Motorcycle().accelerate())
}
运行结果:我的速度是100,我的名字是motorcycle