继承 ★
open class Base(val p: Int)
class Derived(p: Int) : Base(p)
(1) 继承的话, 子类需要继承父类, 子类有责任负责父类字段的初始化
class Derived(p: Int) : Base(p)
(2) 子类最终都会调用到父类的主构造函数用于初始化, 子类主构造函数默认都会调用到父类主构造函数, 所以子类没有主构造函数, 那么次构造函数可以用 super 调用父类的主/次构造函数, 如果子类有构造函数就用 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) {}
}
类如果需要被继承, 这需要显示的写出
open class 类名, 否则默认是public final 类名
覆盖方法(重写方法)
要继承用 open 标记父类
函数要被重写, 父类函数要标记 open
子类重写父类函数, 要使用 override 标记函数
子类不再让open的函数再次被重写, 添加 final 标记该函数
open class Shape {
open fun draw() {}
}
open class Circle : Shape() {
// 如果class Circle是 final 类型(final open Circle), 则函数也是 final类型, 可以不用显示的写出来
final override fun draw() {}
}
覆盖属性
子类与父类有相同的属性名称
open class Shape {
open val vertexCount: Int = 0
}
class Rectangle : Shape() {
override var vertexCount: Int = 1
}
在字段上: (字段可见性永远是 private)
① 使用 val 的话, 字段默认是 private final, 且get/set方法只生成 public final get
② 使用 var 修饰的话, 则是 private 且生成 public final get/set 函数
val ==> private final 字段 ==> public final get/set函数
var ==> private 字段 ==> public final get/set函数
如果在字段上加上 open 的话 , 影响的是 get/set 函数, 使用上 open 函数 get/set 的 final 直接消失
(2) 在父子类间, 子类可以用 var 覆盖父类 val 的同名属性
- 为什么?
因为父类使用 val 属性的话, 该属性只有 get 函数, 而子类默认也可以使用父类该属性的 get 函数, 如果子类使用 var 重写该属性的话, 该属性有了 get/set 函数, 此时不会有任何问题, 子类顶多会重写掉父类的 get 函数, 并且添加 set 函数
如果父类使用 var 修饰属性的话, 该属性有 get/set 函数, 子类会继承使用权, 使用 get/set 函数, 但如果子类使用 val 重写该属性的话, 此时, 子类只想让该属性有 get 函数, 但实际上却有子类的 get 函数和 父类的 set 函数, 明显 val 字段加的没意义, 所以报错
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
}
(1) 调用父类的属性或者方法一般使用 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...")
抽象类★
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()
}
可见性修饰符
类、对象、接⼝、构造函数、⽅法、属性和它们的 setter 都可以有 可⻅性修饰符(getter 总是与属性有着相同的可⻅性)
在 Kotlin 中有这四个可⻅性修饰符:private 、protected 、internal 和 public, 如果没有显示的指定可见性修饰符, 则默认为 public
包
- 如果不指定可见性修饰符, 默认顶层声明都是public
- 如果声明为 private , 那么只能在该文件内使用
- 如果声明为 internal, 那么他将在该模块任何地方使用(如同java的 default)
- protected 包内不支持该修饰符
类和接口
- private: 表明该类内部可见, 包括其内部的所有成员 只能在该类内使用
- protected: 表明该类内的成员在整个类族的子类下可见
- internal: 在整个模块可见
- public: 在任何地方可见
和java相同, 外部类不能访问内部类的 private
如果覆盖一个 protected 成员, 并且没有指定可见性修饰符, 则默认还是 protected 成员
open class Outer {
private val a = 1
protected open val b = 2
internal val c = 3
val d = 4 // 默认 public
protected class Nested {
public val e: Int = 5
}
}
class Subclass : Outer() {
// a 不可⻅
// b、c、d 可⻅
// Nested 和 e 可⻅
override val b = 5 // “b”为 protected
}
class Unrelated(o: Outer) {
// o.a、o.b 不可⻅
// o.c 和 o.d 可⻅(相同模块)
// Outer.Nested 不可⻅,Nested::e 也不可⻅
}
构造函数
要指定⼀个类的的主构造函数的可⻅性,使⽤以下语法(注意你需要添加⼀个显式 constructor 关键字):
class C private constructor(a: Int) { …… }
这⾥的构造函数是私有的。默认情况下,所有构造函数都是 public ,这实际上等于类可⻅的地⽅它就可⻅
局部变量
局部变量, 函数和类不支持可见性修饰符
模块
可⻅性修饰符 internal 意味着该成员只在相同模块内可⻅, 而一个模块是编译在一起的一堆kotlin文件, 比如:
- 一个 idea 模块
- 一个maven 项目
- 一个 gradle 源集(例外是 test 源集可以访问 main 的 internal 声明)
- ⼀次
<kotlinc>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)
}
数据类★
数据类就跟java在Bean相关类中加上 lombok 注解 @Data 的功能差不多
data class User(val name: String, val age: Int)
public final class User {
@NotNull
private final String name;
private final int age;
@NotNull
public final String getName() {
return this.name;
}
public final int getAge() {
return this.age;
}
public User(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
this.age = age;
}
@NotNull
public final String component1() {
return this.name;
}
public final int component2() {
return this.age;
}
@NotNull
public final User copy(@NotNull String name, int age) {
Intrinsics.checkNotNullParameter(name, "name");
return new User(name, age);
}
// $FF: synthetic method
public static User copy$default(User var0, String var1, int var2, int var3, Object var4) {
if ((var3 & 1) != 0) {
var1 = var0.name;
}
if ((var3 & 2) != 0) {
var2 = var0.age;
}
return var0.copy(var1, var2);
}
@NotNull
public String toString() {
return "User(name=" + this.name + ", age=" + this.age + ")";
}
public int hashCode() {
String var10000 = this.name;
return (var10000 != null ? var10000.hashCode() : 0) * 31 + Integer.hashCode(this.age);
}
public boolean equals(@Nullable Object var1) {
if (this != var1) {
if (var1 instanceof User) {
User var2 = (User)var1;
if (Intrinsics.areEqual(this.name, var2.name) && this.age == var2.age) {
return true;
}
}
return false;
} else {
return true;
}
}
}
编译器⾃动从主构造函数中声明的所有属性导出以下成员:
equals() / hashCode();toString()格式是 "User(name=John, age=42)" ;componentN()函数 按声明顺序对应于所有属性;copy()函数。 为了确保⽣成的代码的⼀致性以及有意义的⾏为,数据类必须满⾜以下要求:- 主构造函数需要⾄少有⼀个参数;
- 主构造函数的所有参数需要标记为 val 或 var ;
- 数据类不能是抽象、开放、密封或者内部的;
- (在1.1之前)数据类只能实现接⼝。 此外,成员⽣成遵循关于成员继承的这些规则:
- 如果在数据类体中有显式实现
equals()、hashCode()或者toString(),或者这些函数在⽗类中有final实现,那么不会⽣成这些函数,⽽会使⽤现有函数; - 如果超类型具有
open的componentN()函数并且返回兼容的类型,那么会为数据类⽣成相应的函数,并覆盖超类的实现。如果超类型的这些函数由于签名不兼容或者是final⽽导致⽆法覆盖,那么会报错; - 从⼀个已具
copy(……)函数且签名匹配的类型派⽣⼀个数据类在 Kotlin 1.2 中已弃⽤,并且在 Kotlin 1.3 中已禁⽤。 - 不允许为
componentN()以及copy()函数提供显式实现。
如果代码写成这样: (显示指定默认值)
data class User(val name: String = "", val age: Int = 0)
他就会提供一个无参数构造函数
data class 只使用主构造函数中的字段作为模板生成各种代码, 比如 copy equals 等, 如果属性不在主动构造函数中, 则不会被使用作为生成代码的元素
data class Person(val name: String) {
val age: Int = 0
}
这样 age 不会被用来生成代码
复制
data class User(val name: String = "", val age: Int = 0)
fun main(args: Array<String>) {
val user: User = User("zhazha", 1)
val user1 = user.copy(name = "xixi")
println(user)
println(user1)
}
部分属性填写完毕后, 其他属性仍旧保持以前的值
数据类与解构声明
为数据类⽣成的 Component 函数 使它们可在解构声明中使⽤:
val j = User("zhazha", 22)
val (name, age) = j
println("name = $name, age = $age")
密封类☆
密封类和枚举类对应, 密封类是尽量罗列出所有的有限子类(类型多), 而枚举类是尽量罗列出所有的对象(就是对象多), 而他比枚举类好的地方在于, 密封类子类的属性 在 数量 和 类型上可以不同
使用关键字 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
}
}
内部类和嵌套类 ★
默认是 嵌套类, 内~: 持有外部
this, 需要使用inner声明为内部类 嵌~: 不持有, 是static的
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();
}
}
单独的 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<T>(private val innerList: ArrayList<T> = arrayListOf()) : Collection<T> by innerList
而它实现的函数都是 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()
}
object 关键字 ★
定义一个类并创建类的实例
使用场景:
- 对象声明是定义单例的一种方式.
- 伴生对象可以持有工厂方法和其他与这个类相关, 但在调用时并不依赖类实例的方法. 它们的成员可以通过类名来访问.
- 对象表达式用来替代 Java 的匿名内部类.
单例 ▲
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)
}
伴生对象 ★
伴生对象其实就是一个静态的 公开的 final 的 对象, 而它的方法都不是静态的, 伴生对象之所以看起来像是直接用类名调用, 其实不是, 它底层是
Companion的静态对象, 但编译器可以让它看起来好像不需要调用Companion一样, 在伴生对象作用域内定义的属性也是静态的
使用 companion object 作为标记伴生对象的关键字
private class _User private constructor(val nickName: String) {
companion object {
fun newSubscribingUser(email: String): _User {
return _User(email.substringBefore('@'))
}
fun newFacebookUser(accountId: Int): _User {
fun getFacebookById(accountId: Int) = accountId.toString()
return _User(getFacebookById(accountId))
}
}
}
fun main() {
val user = _User.newSubscribingUser("2033@qq.com")
println(user.nickName)
}
★ 伴生对象是一个单例的静态对象, 伴生对象如果没有名字, 则编译器会生成一个叫 Companion 的静态对象, 并且定义了一个同名的静态对象 Companion, 类名和对象名一致, 代码如下:
private class User02 private constructor(val nickName: String) {
companion object {
fun newSubscribingUser(email: String): User02 {
return User02(email.substringBefore('@'))
}
fun newFacebookUser(accountId: Int): User02 {
fun getFacebookById(accountId: Int) = accountId.toString()
return User02(getFacebookById(accountId))
}
}
}
反编译成 java 是这样:
private final String nickName;
public static final User02.Companion Companion = new User02.Companion((DefaultConstructorMarker)null);
public static final class Companion {
public final User02 newSubscribingUser(@NotNull String email) {}
public final User02 newFacebookUser(int accountId) {}
}
如果有伴生对象有名字, 则把 Companion 的类和相对应的对象全部改成那个名字, 比如下面这段代码就改成了 Loader 静态类 和 Loader 静态对象
open class Person(val name: String) : Serializable {
fun toText(path: String = ClassLoader.getSystemResource("").path + "person.txt"): Person {
val file = File(path)
if (!file.exists()) file.createNewFile()
return ObjectOutputStream(FileOutputStream(path)).use {
it.writeObject(this)
this
}
}
companion object Loader {
val path: String = ClassLoader.getSystemResource("").path
fun fromText(objectPath: String = path + "person.txt") =
ObjectInputStream(FileInputStream(objectPath)).use {
it.readObject() as? Person ?: throw Exception("无法读取到对象")
}
}
}
记得看 path 对象, 它在底层也将被定义成静态字段, 不过它的字段不属于 Loader 类, 属于 Person 类, 但是它的 get/set 属于 Loader 类, 而且 path 属性的字段 和 接口的属性一样, 如果 get/set 函数和该对象没什么关系, 则不会创建path静态字段, 仅保留 get/set 函数在 Loader 静态类中
注意:
伴生对象的类是 final , 属性和方法也是 final 而且也无法用open修改伴生对象的 final 所以子类无法重写
注意:
★ 伴生对象里面的函数不是静态函数, 它仅仅提供了一个静态类和一堆静态对象, fromText 和 getPath 都不是静态的
这些函数最后都会被 伴生对象(注意是 对象 对象 对象) 调用, 所以根本不需要静态, 伴生对象不可以简单的认为他是 java 的 static 静态代码块
如果需要使得伴生变量作为静态函数需要加上
@JvmStatic, 让函数变成Person的静态函数和非静态两个函数(在伴生对象函数的基础上多了个Person的静态函数)
注意:
val person03 = Person.fromText()
也会变成
Person person03 = Person.Loader.fromText$default(Person.Loader, (String)null, 1, (Object)null);
ClassLoader.getSystemResource("").path 将被写入到 static 静态代码块中
伴生对象实现接口▲
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}")
}
对象表达式:改变写法的匿名内部类★
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了个单例