theme: channing-cyan
kotlin
沿用了 java
的单继承系统, 不允许 c++
的多继承出现, 但允许 kotlin
接口的多实现
open class Base(val p: Int)
class Derived(p: Int) : Base(p)
(1) 子类需要继承父类, 子类有责任负责父类字段的初始化
class Derived(p: Int) : Base(p)
(2) 子类最终都需要调用到父类的构造函数以初始化父类的字段, 子类构造函数如果要调用父类构造函数需要使用上 super
(3) 子类构造函数如果要调用到子类的其他构造函数则需要使用到 this
open class Base(val p: Int) {
// name 可以不在这里直接初始化成 "", 可以选择在 init 代码块中, 前面可知, init 中的代码. 最后会合并到主构造函数中
var name: String = ""
constructor(p: Int, name: String): this(p) {
this.name = name
}
}
class Derived : Base {
// 如果子类没有显示的写出主构造函数, 那么次构造函数需要使用 super 主动调用父类的构造函数(主, 次皆可)
constructor(p: Int) : super(p) {}
constructor(p: Int, name: String) : super(p, name) {}
constructor(a: Int, p: Int, name: String) : this(p, name)
}
类如果需要被继承, 这需要显示的写出
open class 类名
, 否则默认是public final 类名
kotlin 所有的类、函数和字段默认都添加上了 final
如果我们需要重写这些功能,可以使用 open
open class Shape {
open fun draw() {}
}
open class Circle : Shape() {
// 如果class Circle是 final 类型(final open Circle), 则函数也是 final类型, 可以不用显示的写出来
final override fun draw() {}
}
open
只针对final
,添加open
会删除掉final
子类与父类有相同的属性名称
open class Shape {
open val vertexCount: Int = 0
}
class Rectangle : Shape() {
override var vertexCount: Int = 1
}
在字段上: (字段可见性默认是 private
)
① 使用 val
只生成 public final get
② 使用 var
生成 public final get/set
如果字段加上 open
, 影响的是 get/set
函数的 final
, 会直接消失
(2) 在父子类间, 子类可以用 var
覆盖父类 val
的同名属性
父类 val
属性只有 getter
方法, 子类使用 var
属性后, 将会生成 getter/setter 方法
, 没有任何问题
如果 父类是 var
那么有 getter/setter 方法
, 此时 子类使用上 val
, 只能有 getter
, 可是父类的 setter
子类仍然可以调用, 这不行
open class Person constructor(open val nickName: String) {
}
class Child(_nickName: String) : Person(_nickName) {
override var nickName: String = _nickName
}
open class Base(val name: String) {
init {
println("2. 父类 init 代码块")
}
open val size: Int = this.name.length.also { println("3. 父类构造函数执行 size 对象初始化") }
}
class Derived(name: String, val lastName: String) :
Base(name.capitalize().also { println("1. Derived的构造函数的初始化代码块 执行初始化") }) {
init {
println("4. Derived 的init代码块执行")
}
override val size: Int =
(super.size + lastName.length).also { println("5. 初始化 Derived 的 size 字段") }
}
fun main(args: Array<String>) {
val derived = Derived("zhazha", "xixi")
}
具体顺序是:
1. Derived的构造函数的初始化代码块 执行初始化
2. 父类 init 代码块
3. 父类构造函数执行 size 对象初始化
4. Derived 的init代码块执行
5. 初始化 Derived 的 size 字段
open class Rectangle {
open fun draw() { println("Drawing a rectangle") }
val borderColor: String get() = "black"
}
class FillRectangle : Rectangle() {
override fun draw() {
// super 调用父类的函数
super.draw()
println("Filling the rectangle")
}
// 调用父类的属性, 这里调用了 getBorderColor() 函数
val fillColor: String get() = this.borderColor
}
super/this
和标签配合选择调用父类函数调用父类的属性或者方法一般使用 super
open class Rectangle {
open fun draw() {
println("Drawing a rectangle")
}
val borderColor: String get() = "black"
}
class FilledRectangle : Rectangle() {
override fun draw() {
// super 调用父类的函数
super.draw()
println("Filling the rectangle")
}
// 调用父类的属性, 这里调用了 getBorderColor() 函数
val fillColor: String get() = this.borderColor
inner class Filler {
fun fill() {
println("Filling ")
}
fun draw() {
println("Filler draw...")
}
fun drawFill() {
// idea 智能提示对这个支持不太好, 需要用户手写完毕, 提示都没有
// 只能支持内部类访问外部类的方法, 访问 FilledRectangle 类的父类方法 draw
super@FilledRectangle.draw()
fill()
println("fillColor = $fillColor")
// 调用 Filler 的 draw
draw()
// 调用 FilledRectangle 的 draw
this@FilledRectangle.draw()
println("class rectangle color: ${super@FilledRectangle.borderColor}")
}
}
}
fun main(args: Array<String>) {
val rectangle = FilledRectangle()
rectangle.draw()
rectangle.Filler().drawFill()
}
① super@AAA类.BB方法()
, 表示调用 AAA类的父类
的BB方法
② this@AAA类.BB方法()
, 表示调用 AAA类
的BB方法
open class Rectangle {
open fun draw() {
println("rectangle draw...")
}
}
interface Polygon {
fun draw()
}
class Square() : Rectangle(), Polygon {
// 虽然两个父类都存在 draw 方法, 但是其中一个是接口, 一个已经存在函数体, 所以直接调用的实现了的方法
}
fun main(args: Array<String>) {
val square = Square()
square.draw()
}
如果是两个类的话, 就需要直接重写一个自己想要的方法了
interface Rectangle {
fun draw() {
println("Rectangle draw...")
}
}
interface Polygon {
fun draw() {
println("Polygon draw...")
}
}
class Square : Rectangle, Polygon {
override fun draw() {
super<Rectangle>.draw()
super<Polygon>.draw()
println("Square draw...")
}
}
fun main(args: Array<String>) {
val square = Square()
square.draw()
}
两个接口的默认函数重名, 子类就需要手动重写该方法, 定义自己的 draw 函数
super<Rectangle>.draw()
super<Polygon>.draw()
println("Square draw...")
多继承会导致继承关系语义上的混乱, 比如下面的情况
interface Horse {
fun run()
}
interface Donkey {
fun run()
}
class Mule : Horse, Donkey {
override fun run() {
TODO("Not yet implemented")
}
override fun run() {
TODO("Not yet implemented")
}
}
我们在定义 Mule 类
的时候需要纠结, 到底实现 马 的 run
还是 实现 驴 的 run
函数, 这两个函数存在区别
马一般跑的比较快, 驴 耐力特别好
到底选择 马 还是 驴 的能力呢?
这就是 棱形继承问题
也叫 钻石问题
, 继承关系将在语义上产生歧义: 骡子到底继承了马的run功能还是驴的run功能呢?
在前面已经有过这个案例了
kotlin
中的类可以实现多个接口
interface Flyer {
fun fly()
fun kind() = "flying animals"
}
interface Animal {
val name: String
fun eat()
fun kind() = "flying animals"
}
private class Bird : Flyer, Animal {
override fun fly() {
println("I can fly")
}
override val name: String
get() = "燕子"
override fun eat() {
println("I can eat")
}
override fun kind(): String {
println(super<Animal>.kind())
println(super<Flyer>.kind())
return "my name is ${this.name}"
}
}
如果存在多个同名的默认方法, 需要通过 super
这种方式指定调用
实现接口的属性和方法都必须加上 override
关键字, 且不能省略(和 java
不同)
上面的源码还可以这么搞:
private class Bird(override val name: String) : Flyer, Animal {}
将接口的属性放到子类的主构造函数中重写
其实接口中的属性只有 setter/getter 函数
, 没有 field 字段
在 Bird
中实现的 name
可以有 field
字段
final class Bird implements Flyer, Animal {
@NotNull
private final String name;
}
当然我们还可以让 name 字
段在 Bird
中不存在, 只保留 getter/setter
private class Bird : Flyer, Animal {
override val name: String
get() = "xiaobai"
}
同样的, 将上面的 Fly
和 Animal
代替成 Horse
和 Donkey
还是会存在 到底 实现哪个 run
的问题
我们可以使用内部类模拟实现多继承的方案, 我们知道, 类内可以定义多个内部类, 每个内部类都可以继承自各个不同的父类, 且每个内部类都是独立存在的, 使用这种特性解决多继承问题
在 java
中只要在一个类内写上另一个类, 我们都可以认为另一个类是内部类
但是在 kotlin
中, 就不一样了
直接在 kotlin
类中写上另一个类A
, 此时类 A
却是 外部类的 嵌套类
, 而不是内部类
name
无法被识别, 说明ErrorInnerKotlin
类不持有外部类的 this
, 否则不会报错
使用嵌套类方便序列化和反序列化, 不过这是后话
kotlin
中的内部类是这样的:
需要使用 inner
修饰内部类
在 java
中如果需要实现一个嵌套类
需要配合 static
关键字, 而 kotlin
则相反, 默认是嵌套类
, 需要加上 inner
才能变成内部类
嵌套类和内部类的区别
在于: 是否持有外部类
的 this
引用, 就如前面的代码显示, 如果不持有 外部类
的 this
, name
无法被访问, 而嵌套类
就是这样的…
interface Horse {
fun run()
}
interface Donkey {
fun run()
}
class Mule {
fun run() {
HorseC().run()
print(" and ")
DonkeyC().run()
println()
}
private inner class HorseC : Horse {
override fun run() {
print("I can run fast")
}
}
private inner class DonkeyC : Donkey {
override fun run() {
print("I can run long time")
}
}
}
通过这个案例改造后我们发现:
HorseC
和 DonkeyC
分别继承Horse
和Donkey
这两个外部类, 我们就可以在Mule
类中定义他们的实例对象, 从而获得他们两者不同的状态和行为(那为什么不直接定义两个属性到 Mule
中??? 因为是接口么?)private
修饰内部的 HorseC
和 DonkeyC
让外部类不至于直接访问到内部类. 提供了更好的封装性因此在某些场景下, 内部类是解决多继承问题非常好的思路
这种方式我们可以在很多框架的源码中发现
委托在 kotlin
用的还是比较多的, 比如: 委托的观察者, 可以观察属性的修改和读取行为
现在来试试 委托如何代替多继承
interface CanFly {
fun fly()
}
interface CanEat {
fun eat()
}
open class Flyer : CanFly {
override fun fly() {
println("I can fly")
}
}
open class Animal : CanEat {
override fun eat() {
println("I can eat")
}
}
class Bird(flyer: Flyer, animal: Animal) : CanFly by flyer, CanEat by animal {
}
fun main() {
val flyer = Flyer()
val animal = Animal()
val bird = Bird(flyer, animal)
bird.fly()
bird.eat()
}
这种方式和前面接口实现多继承的方式相似, 那有什么有点呢?
A
和 BC
对象的方法, 而是 A.method
这种形式, 更加的只管, 虽然背后也是通过委托对象来执行具体的方法逻辑的数据类就跟java
在Bean
相关类中加上 lombok
注解 @Data
的功能差不多
data class Bird(var weight: Double, var age: Int, var color: String)
反编译成 java :
public final class Bird {
private double weight;
private int age;
@NotNull
private String color;
public final double getWeight() {
return this.weight;
}
public final void setWeight(double var1) {
this.weight = var1;
}
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
@NotNull
public final String getColor() {
return this.color;
}
public final void setColor(@NotNull String var1) {
this.color = var1;
}
public Bird(double weight, int age, @NotNull String color) {
super();
this.weight = weight;
this.age = age;
this.color = color;
}
public final double component1() {
return this.weight;
}
public final int component2() {
return this.age;
}
@NotNull
public final String component3() {
return this.color;
}
@NotNull
public final Bird copy(double weight, int age, @NotNull String color) {
return new Bird(weight, age, color);
}
// $FF: synthetic method
public static Bird copy$default(Bird var0, double var1, int var3, String var4, int var5, Object var6) {
if ((var5 & 1) != 0) {
var1 = var0.weight;
}
if ((var5 & 2) != 0) {
var3 = var0.age;
}
if ((var5 & 4) != 0) {
var4 = var0.color;
}
return var0.copy(var1, var3, var4);
}
@NotNull
public String toString() {
return "Bird(weight=" + this.weight + ", age=" + this.age + ", color=" + this.color + ")";
}
public int hashCode() {
int var10000 = (Double.hashCode(this.weight) * 31 + Integer.hashCode(this.age)) * 31;
String var10001 = this.color;
return var10000 + (var10001 != null ? var10001.hashCode() : 0);
}
public boolean equals(@Nullable Object var1) {
if (this != var1) {
if (var1 instanceof Bird) {
Bird var2 = (Bird)var1;
if (Double.compare(this.weight, var2.weight) == 0 && this.age == var2.age && Intrinsics.areEqual(this.color, var2.color)) {
return true;
}
}
return false;
} else {
return true;
}
}
}
这比 javaBean
舒服多了
很多人会说, 还
javaBean
??? 为什么不用lombok
呢? 深有同感,kotlin
的data class
其实不太方便
copy
方法的主要作用是: 从已有的数据类对象中拷贝一个新的数据类对象
需要注意这是
浅拷贝
, 如果data class
中有引用对象, 需要注意了
既然都
copy
了? 那我为什么不用MapStruct
???
componentN
与解构说到底, component
就是以 data class
声明的顺序定义的对象, 从 1
开始, 到最高的 5
如果在 java
我们可能会这样使用一个对象接收一个函数的返回值对象, 然后再分别赋值给各个变量
val b1 = Bird(20.0, 1, "blue")
val weight = b1.weight
val age = b1.age
val color = b1.color
在 kotlin
中:
val (weight, age, color) = b1
解构的方式还可以用于下面这样:
val birdInfo = "20.0,1,bule"
val (weight, age, color) = birdInfo.split(",")
println("weight = $weight, age = $age, color = $color")
这种神器的方法就是别的语言常说的解构, 一种通过与编译器达成约定的功能
data class Bird(var weight: Double, var age: Int, var color: String) {
var sex = 1
operator fun component4(): Int = this.sex
}
kotlin
还提供了 元组, 比如 Pair
和Triple
其中 Pair
是二元组, Triple
是三元组
其底层都是
data class
, 而且每个元素可以是任意类型
使用数据类必须满足以下条件:
如果代码写成这样: (显示指定默认值)
data class Bird(var weight: Double = 0.0, var age: Int = 0, var color: String = "red")
他就会提供一个无参数构造函数
data class 只使用主构造函数中的字段作为模板生成各种代码, 比如 copy equals 等, 如果属性不在主动构造函数中, 则不会被使用作为生成代码的元素
data class Person(val name: String) {
val age: Int = 0
}
这样 age
不会被用来生成代码
data class
目前还是不太好用, 比较局限, 对于很多框架还有兼容性问题, 我看很多人都没用上, 连我也是
abstract class Polygon {
abstract fun draw()
}
class Rectangle : Polygon() {
override fun draw() {
println("Rectangle draw...")
}
}
还可以用抽象成员函数覆盖非抽象的open
成员函数
open class Polygon {
open fun draw() {
println("Polygon draw...")
}
}
abstract class Rectangle : Polygon() {
// 子类将父类的open方法重写成 abstract
abstract override fun draw()
}
kotlin
和 java
的可见性修饰符差不多, 但也有区别:
kotlin
和 java
的默认修饰符不同, java
默认是包(default
), 而 kotlin
默认是 public
kotlin
有独特的修饰符 internal
kotlin
可以在一个文件内单独声明方法及产量, 同样支持可见性修饰符Java
中除了内部类可以用private
修饰以外, 其他类都不允许private
修饰, 而 kotlin
可以.kotlin
和java
中的 protected
的访问范围不同, java
中是包, 类以及子类可访问, 而 kotlin
只允许类及子类kotlin
省掉了 java
每次写个类都需要加上 public
的问题, 虽然每次它都会自动加上
java
中 默认类都是 default
包访问范围, kotlin
中也有 internal
跟其配对, 但还是有不同, internal
是模块内可见
那么什么是模块内可见呢?
maven
项目gradle
项目eclipse
项目idea
项目Ant
任务执行编译的代码这些都被 kotlin
看作的 模块
一个文件可以看作一组编译的文件组成的集合
internal
而不是包内访问 default
?java
默认包类存在的问题:
假如有一个包类 String
, 包路径是 java.lang
, 并被打包成库src.jar
package java.lang.String;
/* public */ class String {
public void print() {}
/**
内容略
*/
}
此时我们要在库外部使用 String
类的功能print
, 只有两种方式
src.jar
库, 然后 copy 库中的String
类的源码, 放在自己的包路径中src.jar
, 在我们的项目中创建 java.lang
包, 此时我们可以在该包路径中定义类, 并直接使用 String
上一次我这么用还是在探讨
jvm
的双亲委派机制
在 kotlin
中 没有选择 java
的包内机制, 而是模块内机制
, 只和 该类
一起编译的其他 kotlin
文件可见
而开发工程和第三方类库不属于同一个模块, 此时还要使用该类的话, 只能拷贝源码了
java
中没有文件内私有类, kotlin
有Java 中没有文件内私有类(private class
) 而 kotlin
中有
主要的问题是 java
中一个文件只能有一个大类(public类
), 可以有private
的子类
而 kotlin
中, 可以有一个大类, 而大类下面还可以写顶层函数或属性
class BMWCar(val name: String) {
private val bMWEngine: BMWEngine = BMWEngine()
}
private class BMWEngine {
fun engineName() {
println("BMWEngine")
}
}
kotlin
中没有包作用域概念在 kotlin
中没有包作用域, 所以 java
的 protected
到了 kotlin
中没有访问包作用域的功能, 或者说没有 internal
的功能(internal
是模块作用域
)
所以kotlin
的 protected 标记的类在同一个包的其他文件中无法使用
要指定⼀个类的的主构造函数的可⻅性,使⽤以下语法(注意你需要添加⼀个显式 constructor
关键字):
class C private constructor(a: Int) { …… }
这⾥的构造函数是私有的。默认情况下,所有构造函数都是 public
,这实际上等于类可⻅的地⽅它就可⻅
局部变量, 函数和类不支持可见性修饰符
可⻅性修饰符 internal
意味着该成员只在相同模块内可⻅, 而一个模块是编译在一起的一堆kotlin
文件, 比如:
idea
模块maven
项目gradle
源集(例外是 test
源集可以访问 main
的 internal
声明)
Ant
任务执⾏所编译的⼀套⽂件internal class Button : View {
private fun yell() = println("Hey!")
protected fun whisper() = println("Let's go!")
}
fun Button.giveSpeech() { // 错误, 企图把public 类 转成 internal 类
println(this::class.java)
}
但
internal
比较尴尬, 翻译成Java
字节码后被认为是public
密封类和枚举类对应, 密封类是尽量罗列出所有的有限子类(类型多), 而枚举类是尽量罗列出所有的对象(就是对象多), 而他比枚举类好的地方在于, 密封类子类的属性 在 数量 和 类型上可以不同
使用关键字 sealed
修饰符作为声明密封类的方法
所有继承密封类的子类都必须放在同一个文件中
sealed class Expr
data class Const(val number: Double) : Expr() {
}
data class Sum(val e1: Expr, val e2: Expr) : Expr()
object NotANumber : Expr()
fun main(args: Array<String>) {
// 应用场景
fun eval(expr: Expr): Double = when(expr) {
is Const -> expr.number
is Sum -> eval(expr.e1) + eval(expr.e2)
NotANumber -> Double.NaN
}
}
密封类可以看成是一个功能强大的枚举类
前面说过的:
kotlin
默认是嵌套类
, 不持有外部类的this
引用, 类似于static
修饰的内部类: 持有外部
this
, 需要使用inner
声明为内部类
kotlin 类内部可以有类, 而内外类的关系有两种, 一种叫嵌套类(默认的), 另一种叫内部类(需要使用 inner 修饰符)
kotlin的嵌套类反编译成 java 后显示是 static , 这样的好处在于下面这个案例
interface State : Serializable
interface View {
fun getCurrentState(): State
fun restoreState(state: State)
}
这时候如果是 java 代码的话, 实现方式:
public class Button02 implements View {
@Override
public State getCurrentState() {
return new ButtonState(20, "name");
}
@Override
public void restoreState(State state) {
}
class ButtonState implements State {
private Integer age;
private String name;
public ButtonState(Integer age, String name) {
this.age = age;
this.name = name;
}
}
public static void main(String[] args) throws Exception {
Button02 button02 = new Button02();
System.out.println(button02.getCurrentState());
ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("D://test.txt"));
// 他会在这里报错 NotSerializableException
outputStream.writeObject(button02.getCurrentState());
outputStream.flush();
outputStream.close();
}
}
在java中单独的 ButtonState
是可以序列化的, 但此处的 ButtonState
是内部类, 内部类偷偷的藏了个 外部类的 this
对象, 该对象不支持 序列化 , 所以报错了
只要把 ButtonState
改成 static
后就不会报错了
static class ButtonState implements State
而在 kotlin 中就不会出现这种问题
kotlin写在类内部的类, 默认是 嵌套类, 同时也是 static
类, 这样内部不会存放 外部类的 this
private class Button01 : View {
lateinit var buttonState: State
override fun getCurrentState(): State = ButtonState(12, "haha")
override fun restoreState(state: State) {
this.buttonState = state
}
private class ButtonState(_age: Int, _name: String) : State {
val age: Int = _age
val name: String = _name
}
}
fun main() {
val button01 = Button01()
with(ObjectOutputStream(File("""D:\test.txt""").outputStream())) {
writeObject(button01.getCurrentState())
flush()
close()
}
// ObjectOutputStream(File("""D:\test.txt""").outputStream()).apply {
// writeObject(button01.getCurrentState())
// flush()
// close()
// }
}
而 kotlin 使用 inner
修饰符定义内部类
inner class ButtonState02 {
}
委托有很多, 这里只讲类委托, 后续再整个讲一遍
一个类借助另一个类的对象
实现接口的函数, 说白了就是借鸡生蛋
下面是不委托的情况下:
class DelegateCollectionDemo01<T> : Collection<T> {
private val innerList = arrayListOf<T>()
override val size: Int
get() = innerList.size
override fun contains(element: T): Boolean = innerList.contains(element)
override fun containsAll(elements: Collection<T>): Boolean= innerList.containsAll(elements)
override fun isEmpty(): Boolean = innerList.isNotEmpty()
override fun iterator(): Iterator<T> = innerList.iterator()
}
下面的委托的情况:
class DelegateCollectionDemo02<T>(private val innerList: ArrayList<T> = arrayListOf()) : Collection<T> by innerList { }
fun main() {
val collection01 = DelegateCollectionDemo01<Int>()
val collection02 = DelegateCollectionDemo02<Int>()
}
核心代码:
class DelegateCollectionDemo02
而它实现的函数都是 Collection
接口的函数, 而不是 innerList
的 ArrayList
的函数
Collection
的函数一致, 如果Collection
被更改或者新增了新的函数, DelegateCollection
也不用修改, 代码自动生成的innerList
对象的函数, 那么可以 override 重写改函数, 添加自己的方法class DelegateCollectionDemo02<T>(private val innerList: ArrayList<T> = arrayListOf()) : Collection<T> by innerList {
override fun isEmpty(): Boolean = innerList.isNotEmpty()
}
注意: 委托类不能委托接口默认函数, 因为
kotlin
接口默认没有默认函数, 而是使用静态类实现类似效果
object
关键字 ★
kotlin
中没有staitc关键字
, 它引入了新的方法object 关键字
, 它可以完美的代替static
的所有场景, 并且添加了很多新的功能
上面的代码虽然没什么问题, 但是 static
和 普通 方法的代码混在在一起, 比较难以区分, 虽然它有 static
作为标记
static
方法是 类 的方法, 而普通方法是对象的方法, 这两有着本质的区别
所以在 kotlin
中做了区分
表示跟随着类对象而出现, 它是属于这个类的Class对象所拥有, 因此它是个单例对象, 伴生对象需要声明在类的内部, 在类被加载时初始化
class Prize(
val name: String,
val count: Int,
val type: Int
) {
companion object {
val TYPE_REDPACK = 0
val TYPE_COUPON = 1
fun isRedpack(prize: Prize): Boolean {
return prize.type == TYPE_REDPACK
}
}
}
fun main() {
val prize = Prize("红包", 10, Prize.TYPE_REDPACK)
println(Prize.isRedpack(prize))
}
可以看到静态的属性和方法都被存储到 companion object
中了, 代码变得更加的清晰
并且我们在调用 companion object
内的 isRedpack
方法也非常的方便Prize.isRedpack(prize)
, 不需要Prize.Companion.isRedpack(prize)
class Prize(val name: String, val count: Int, val type: Int) {
companion object {
val TYPE_COMMON = 1
val TYPE_REDPACK = 2
val TYPE_COUPON = 3
val defaultCommonPrize = Prize("ptjp", 19, Prize.TYPE_COMMON)
fun newRedpackPrize(name: String, count: Int) = Prize(name, count, Prize.TYPE_REDPACK)
fun newCouponPrize(name: String, count: Int) = Prize(name, count, Prize.TYPE_COUPON)
fun defaultCommonPrize() = defaultCommonPrize
}
}
fun main() {
val redpackPrize = Prize.newRedpackPrize("红包", 10)
val couponPrize = Prize.newCouponPrize("十元代金券", 10)
val commonPrize = Prize.defaultCommonPrize()
}
class CompanionObjectDemo02 {
interface ObjectFactory<T> {
fun writeObject(t: T)
fun loadObject(text: String = ClassLoader.getSystemResource("").path + "person02.txt"): T
}
open class Person(val name: String) : Serializable {
companion object : ObjectFactory<Person> {
// override fun loadObject(text: String): Person = File(text).let { it ->
// if (!it.exists()) it.createNewFile()
// ObjectInputStream(it.inputStream()).use {
// it.readObject() as? Person ?: throw Exception("文件加载失败")
// }
// }
override fun loadObject(text: String): Person = File(text).run {
myExists()
ObjectInputStream(inputStream()).use {
it.readObject() as? Person ?: throw Exception("文件加载失败")
}
}
private fun File.myExists() {
if (!exists()) createNewFile()
}
override fun writeObject(t: Person) = with(File(ClassLoader.getSystemResource("").path + "person02.txt")) {
myExists()
ObjectOutputStream(outputStream()).use {
it.writeObject(t)
}
}
// override fun writeObject(t: Person) = File(ClassLoader.getSystemResource("").path + "person02.txt").run {
// myExists()
// ObjectOutputStream(outputStream()).use {
// it.writeObject(t)
// }
// }
}
}
}
fun main() {
CompanionObjectDemo02.Person.writeObject(CompanionObjectDemo02.Person("haha"))
val person = CompanionObjectDemo02.Person.loadObject()
println(person)
}
@JvmStatic
, 但该注解不能添加到objects
或者companion object
之外的地方, ① 如果注解到字段上, 该字段的get/set
函数变成静态函数; ② 如果修饰到函数上, 函数将变成静态的
@JvmField
, 添加了该注解, ①字段访问修饰符将从private
变成public
; ② 该注解可以在伴生对象以外的地方使用
class CompanionObjExtensionDemo01 {
class Person(val name: String) : Serializable {
companion object {
}
}
}
private fun File.myExists() {
if (!exists()) createNewFile()
}
private fun CompanionObjExtensionDemo01.Person.Companion.loadObject(path: String = ClassLoader.getSystemResource("").path + "person03.txt"): CompanionObjExtensionDemo01.Person =
with(File(ClassLoader.getSystemResource("").path + "person03.txt")) {
myExists()
ObjectInputStream(inputStream()).use {
it.readObject() as? CompanionObjExtensionDemo01.Person ?: throw Exception("文件加载失败")
}
}
private fun CompanionObjExtensionDemo01.Person.Companion.writeObject(t: CompanionObjExtensionDemo01.Person) =
with(File(ClassLoader.getSystemResource("").path + "person03.txt")) {
myExists()
ObjectOutputStream(outputStream()).use {
it.writeObject(t)
}
}
fun main() {
val person = CompanionObjExtensionDemo01.Person("haha")
CompanionObjExtensionDemo01.Person.writeObject(person)
val person1 = CompanionObjExtensionDemo01.Person.loadObject()
println("person = ${person.name}, person01 = ${person1.name}")
}
在 java 中实现单例的方法有很多:
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
public class Singleton {
private volatile static Singleton singleton;
private Singleton (){}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
而在 kotlin 中由于 object
的存在, 我们可以直接用它来实现单例
object Singleton {
}
java源码:
public final class Singleton {
@NotNull
public static final Singleton INSTANCE;
private Singleton() {
}
static {
Singleton var0 = new Singleton();
INSTANCE = var0;
}
}
这种一种饿汉模式, 只要类被加载就会直接实例化单例
单例对象还可以继承别的接口或者类
object CaseInsensitiveFileComparator : Comparator<File> {
override fun compare(o1: File, o2: File): Int = o1.path.compareTo(o2.path, true)
}
private interface ObjectFactory<T> {
fun writeObject(t: T)
fun loadObject(text: String = ClassLoader.getSystemResource("").path + "person02.txt"): T
}
private class Person02(val name: String) : Serializable
private fun File.myExists() {
if (!exists()) createNewFile()
}
fun main() {
val obj: ObjectFactory<Person02> = object : ObjectFactory<Person02> {
override fun writeObject(t: Person02) =
with(File(ClassLoader.getSystemResource("").path + "person04.txt")) {
myExists()
ObjectOutputStream(outputStream()).use {
it.writeObject(t)
}
}
override fun loadObject(text: String): Person02 =
with(File(ClassLoader.getSystemResource("").path + "person04.txt")) {
myExists()
ObjectInputStream(inputStream()).use {
it.readObject() as? Person02 ?: throw Exception("文件加载失败")
}
}
}
val person02 = Person02("haha")
obj.writeObject(person02)
val person021 = obj.loadObject()
println("person02 = ${person02.name}, person021 = ${person021.name}")
}
注意:
final
限制, 可以直接在匿名内部类中修改其值总结:
object
关键字如果 借助:
修饰表示给object
加上该类型, 而整体来看就是 new 一个该接口类型的子类对象, 这样我们可以得出结论,object
如果确定了类型, 那功能是定义一个类, 并new
出一个对象
, 如果修饰类名则默认为定义了个类, 并new
了个单例