系列文章路引 👀 https://blog.csdn.net/weixin_44235109/article/details/119680974
文章目录
一、kotlin类的构造器?
1.构造器的基本写法与java对比?
示例:kotlin类构造器的常规写法
//大括号包含的即是kotlin的构造器了,声明了两个属性
//1.age 类内全局可见 2.构造器内(init块,属性初始化)
// 类的构造器 构造器参数 同时也定义了属性
class Person constructor(var age: Int, var str: String)
// 省略写法
class Person(var age: Int, name: String)
这么看下去有点懵?我们翻译成java代码看一下
示例:翻译为java代码如下
public final class Person {
private int age;
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
public Person(int age, @NotNull String name) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.age = age;
}
}
这就看得懂了吧,java需要这么多行的代码,kotlin一行就搞定了。是不是很方便。
那有些小伙伴就问了,那如果我想在java的构造函数中处理一些逻辑,kotlin怎么做到的呢?
那这个时候init代码块就出现了,具体是什么,我们一块来看看吧~
2.init代码块
示例:init代码块
class Person(var age: Int, name: String) {
var name: String? = null
init {
println(name)
}
init {
val nameStr = name
this.name = nameStr
}
}
我们可以看到init代码块可以访问到构造器传入的参数,那么他是怎么样被调用的呢?对应的java代码是什么呢?我们反编译成java代码看一下
public final class Person {
@Nullable
private String name;
private int age;
@Nullable
public final String getName() {
return this.name;
}
public final void setName(@Nullable String var1) {
this.name = var1;
}
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
public Person(int age, @NotNull String name) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.age = age;
boolean var3 = false;
System.out.println(name);
this.name = name;
}
}
可以很明显的看出,init代码块在Java平台其实就是相当于构造函数的执行体,且如果有多个init块,是按照顺序在构造函数中执行的。
3.kotlin副构造器
定义:定义了主构造器后在类内部定义的构造器统称为副构造器(定义的副构造器需要调用自身(最终调用到主构造器)或者父类的构造器)
示例:副构造器的简单示例
class Person(var age: Int, name: String) {
constructor(age: Int) : this(age, "zxf"){
println("i am sub-constructor")
}
init {
println(name)
}
}
我们看一下对应的java代码如何
public final class Person {
private int age;
public final int getAge() {
return this.age;
}
public final void setAge(int var1) {
this.age = var1;
}
public Person(int age, @NotNull String name) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.age = age;
boolean var3 = false;
System.out.println(name);
}
public Person(int age) {
this(age, "zxf");
String var2 = "i am sub-constructor";
boolean var3 = false;
System.out.println(var2);
}
}
对应的java代码其实就是声明了一个对应的构造器,且调用了“主”构造器。
另外看得出,init代码块只在主构造器中被调用,且因为副构造器代码块中的第一行代码。首先调用了“主”构造器。
所以代码执行顺序:init代码块要先与副构造器执行。
4.kotlin构造器的推荐使用方式
- 不推荐不定义主构造器只定义副构造器
- 推荐主构造器填上默认参数
推荐示例
class Person(var age: Int, name: String = "zxf")
//主构造器默认参数在java代码中可以以重载的形式调用
class Person1 @JvmOverloads constructor(var age: Int, name: String = "zxf", height: Int = 185)
二、Kotlin与Java类与成员的可见性对比?
1.Kotlin与Java可见性修饰符对比?
可见性表格对比:
可见性对比 | Java | Kotlin |
---|---|---|
public | 公开 | 与java相同,默认即为public |
internal | × | 模块内可见 |
default | 包内可见,默认 | × |
protected | 包内以及子类可见 | 类内以及子类可见(不可修饰顶级类) |
private | 类内可见 | 类内或者文件内可见 |
模块内通常来说是一个jar包或者aar,比如:
- Intellij IDEA 模块
- Maven 工程
- Gradle SourceSet
- Ant 任务中一次调用 <Kotlinc>文件
下面针对kotlin的特有的修饰符,看一下可修饰的对象
可见性类型 | 顶级声明 | 类 | 成员 |
---|---|---|---|
public | √ | √ | √ |
internal | √(模块内可见) | √(模块内可见) | √(模块内可见) |
protected | × | √(可修饰内部类,类、以及子类可见) | √(类、以及子类可见) |
private | √(文件) | √(文件) | √(类) |
顶级声明:指文件内直接定义的属性、函数、类、等
internal VS default
- 一般由SDK或者公共组件开发者用于隐藏模块内部细节实现
- default 可通过外部创建相同包名来进行访问,访问控制非常弱
- default 会导致不同抽象汇聚到相同的包下面
- internal 可方便处理内外隔离,提升模块代码内聚减少接口暴露
- internal 修饰的kotlin类或者成员在java当中可直接访问
2.Kotlin构造器以及属性的可见性?
正常的构造器默认都是public,那如何控制构造器的访问可见性呢?如下所示:
//默认为public修饰
class Person(val name: String, val age: Int)
//构造器私有化
class Person private constructor(val name: String, val age: Int)
属性可见性
//私有化属性,外部不可以进行访问
class Peron(private var age:Int,var name:String)
- getter的可见性必须与属性保持一致
- setter的可见性不得大于属性的可见性
如下所示:
三、kotlin类属性的延迟初始化?
1.为什么要延迟初始化?
类属性必须在构造时进行初始化,但是某些成员只有在类构造之后才会有值,所以需要延迟初始化
2.定义可null类型延迟初始化
如下所示:
private var name: String? = null
fun onCreate() {
//赋值
name = "zxf"
//使用
useName(name!!)
useName(name ?: "")
}
fun useName(name: String) {
// TODO: 2021/7/5 do
}
3.使用lateinit初始化
如下所示:使用处不再需要空指针判断
private lateinit var name: String
fun onCreate() {
//赋值
name = "zxf"
//使用
useName(name)
useName(name)
}
fun useName(name: String) {
// TODO: 2021/7/5 tdo
}
注意:使用lateinit必须知道属性初始化以及使用的生命周期,如果在属性还没有初始化的时候进行使用,则会出现如下的异常:lateinit property name has not been initialized
Exception in thread "main" kotlin.UninitializedPropertyAccessException: lateinit property name has not been initialized
at org.example.zxf.kotlin5.DelayInitTest.onCreate(DelayInit.kt:22)
at org.example.zxf.kotlin5.DelayInitKt.main(DelayInit.kt:13)
at org.example.zxf.kotlin5.DelayInitKt.main(DelayInit.kt)
另外,可以使用isInitialized判断属性是否初始化
如下所示
fun onCreate() {
//判断是否初始化
if (::name.isInitialized){
name = "zxf"
}
//使用
useName(name)
}
4.使用lazy延迟初始化(推荐)
如下所示:
class Personal4(var age: Int) {
// 延迟初始化 只有在该属性第一次被访问到时,进行初始化
val name: String by lazy {
"zxf"
}
}
fun main() {
// 使用该属性
println(Personal4(2).name)
}
注意:如果确保被访问的属性不存在线程安全的问题,可传入LazyThreadSafetyMode.NONE参数,因为默认lazy使用synchronized做同步,若不存在线程安全的问题,传入LazyThreadSafetyMode.NONE可不做同步。
如下所示:
class Personal4(var age: Int) {
// 延迟初始化 只有在该属性第一次被访问到时,进行初始化
val name: String by lazy(LazyThreadSafetyMode.NONE) {
"zxf"
}
}
fun main() {
// 使用该属性
// println(Personal4(2).name)
DelayInitTest().onCreate()
}
5.延迟初始化方案对比
方案名称 | 推荐指数 | 理由 |
---|---|---|
可空类型 | ⭐⭐ | 增加代码复杂度;初始化与声明分离;调用处需要做判空处理 |
lateinit | ⭐⭐⭐ | 初始化与声明分离;调用处无需判空处理,但潜在的初始化问题可能被掩盖 |
lazy | ⭐⭐⭐⭐⭐ | 初始化与声明内聚;无需声明可空类型 |
四、Kotlin代理Delegate
1.代理是什么?
接口代理:对象X代替当前类A实现接口B的方法
属性代理:对象X代替属性a实现getter/setter方法
关键字by:by关键字实际上就是一个代理运算符重载的符号,任何一个具备代理规则的类,都可以使用by关键字进行代理。
2.接口代理
使用传入的对象去代理接口。
如下所示:
//定义接口
interface Api {
fun a()
fun b()
fun c()
}
//定义实现类1
class ApiImpl : Api {
override fun a() {}
override fun b() {}
override fun c() {
println("c is ApiImpl.")
}
}
//定义实现类2
class ApiImpl1 : Api {
override fun a() {}
override fun b() {}
override fun c() {
println("c is ApiImpl1.")
}
}
//使用接口代理实现类包装
class ApiWrapper(private val api: Api) : Api by api {
override fun c() {
println("c is ApiWrapper.")
api.c()
}
}
//使用以及结果
fun main(){
ApiWrapper(ApiImpl()).c()
ApiWrapper(ApiImpl1()).c()
}
c is ApiWrapper.
c is ApiImpl.
c is ApiWrapper.
c is ApiImpl1.
2.属性代理
属性代理基本理解
属性代理是借助于代理设计模式,把这个模式应用于一个属性时,它可以将访问器的逻辑代理给一个辅助对象。
可以简单理解为属性的setter、getter访问器内部实现是交给一个代理对象来实现,相当于使用一个代理对象来替换了原来简单属性字段读写过程,而暴露外部属性操作还是不变的,照样是属性赋值和读取,只是setter、getter内部具体实现变了
属性代理的基本格式:
class Student{
var name: String by Delegate()
}
class Delegate{
operator fun <T> getValue(thisRef: Any?, property: KProperty<*>): T{
...
}
operator fun <T> setValue(thisRef: Any?, property: KProperty<*>, value: T){
...
}
}
属性name将它访问器的逻辑委托给了Delegate对象,通过by关键字对表达式Delegate()求值获取这个对象。任何符合属性代理规则都可以使用by关键字。属性代理类必须要遵循getValue(),setValue()方法约定,getValue、setValue方法可以是普通方法也可以是扩展方法,并且是方法是支持运算符重载。如果是val修饰的属性只需要具备getValue()方法即可。
属性代理基本流程就是代理类中的getValue()方法包含属性getter访问器的逻辑实现,setValue()方法包含了属性setter访问器的逻辑实现。当属性name执行赋值操作时,会触发属性setter访问器,然后在setter访问器内部调用delegate对象的setValue()方法;执行读取属性name操作时,会在getter访问器中调用delegate对象的getValue方法.
常见属性代理基本使用
属性代理是Kotlin独有的特性,我们自己去自定义属性代理,当然Kotlin还提供了几种常见的属性代理实现。例如:lazy,Delegates.observable(), Delegates.vetoable() **属性代理-lazy**:代理主要用于可以初始化时候初始化而是可以延迟到之后再初始,这个在上面将延迟初始化的时候已经提到过了,这里就不多做介绍了。 **属性代理-observable**:Delegates.observable()主要用于监控属性值发生变更,类似于一个观察者。当属性值被修改后会往外部抛出一个变更的回调。它需要传入两个参数,一个是initValue初始化的值,另一个就是回调lamba, 回调出property, oldValue, newValue三个参数。示例如下:
// 属性代理 observable
class StateManager {
// initialValue 初始值
var state: Int by Delegates.observable(0) {
// set 的时候执行当前的表达式
property, oldValue, newValue ->
println(
"""property:$property
oldValue:$oldValue
newValue:$newValue
""".trimMargin()
)
}
}
//使用
fun main() {
val stateManager = StateManager()
stateManager.state = 10
stateManager.state = 11
}
//输出
property:var org.example.zxf.kotlin5.StateManager.state: kotlin.Int
oldValue:0
newValue:10
property:var org.example.zxf.kotlin5.StateManager.state: kotlin.Int
oldValue:10
newValue:11
属性代理-vetoable:Delegates.vetoable()代理主要用于监控属性值发生变更,类似于一个观察者,当属性值被修改后会往外部抛出一个变更的回调。它需要传入两个参数,一个是initValue初始化的值,另一个就是回调lamba, 回调出property, oldValue, newValue三个参数。与observable不同的是这个回调会返回一个Boolean值,来决定此次属性值是否执行修改。
示例如下:
class StateManager {
var state2: Int by Delegates.vetoable(0) {
property, oldValue, newValue ->
return@vetoable newValue == 1
}
}
//使用
fun main(){
val stateManager2 = StateManager()
stateManager2.state2 = 1
stateManager2.state2 = 2
println("stateManager2 state2 is :${stateManager2.state2}")
}
//输出
stateManager2 state2 is :1
五、Kotlin单例object
0.object、companion object以及val和const val的区别
object单例
默认有一个INSTATIC的静态成员常量(static final),在静态代码块中,分配自己(new 自己)
成员变量 val 相当于 java类里面的 声明了静态常量(static final),在静态代码块中初始化值。
成员变量var相当于java类里面的 声明了静态变量(static),在静态代码块中初始化值。
方法都是对象方法。
companion object伴生对象理论上来说和object一样。
在当前类里面创建了一个Companion 的类,在当前类中,持有一个静态常量(static final),静态字段初始化的时候new Companion 对象。
里面的成员变量val 和 var 和上面的object相似,不过是在当前类中声明的,在当前类的静态代码块中初始化。
方法在用final修饰的,在Companion 对象内部声明。
这里面的 const val 和 val的区别。
const val 是以静态字段的形式初始化(编译期常量);
val 在静态代码块中初始化(运行时常量);
1.单例的多种实现方式
饿汉式:线程安全,可用。但类一加载的时候,就实例化,提前占用了系统资源
示例如下(Java):
public class Singleton {
private Singleton() {
}
private static Singleton sSingleton = new Singleton();
public static Singleton getInstance() {
return sSingleton;
}
}
懒汉式:一般配合双重检查锁 DCL使用,保证线程安全/font>
示例如下(Java):
public class Singleton {
private Singleton() {
}
private static volatile Singleton sSingleton;
public static Singleton getInstance() {
if (sSingleton == null) {
synchronized (Singleton.class) {
// 未初始化,则初始instance变量
if (sSingleton == null) {
sSingleton = new Singleton();
}
}
}
return sSingleton;
}
}
静态内部类:使用时才会加载,且加载类时初始化,线程安全
示例如下(Java):
public class Singleton {
private Singleton () {
}
private static class InnerClassSingleton {
private final static Singleton sSingleton = new Singleton();
}
public static Singleton getInstance() {
return InnerClassSingleton.sSingleton;
}
}
2.kotlin的单例模式object
单例模式:kotlin使用object修饰的单例模式采用的是饿汉式
示例如下
object Singleton {
}
反编译为java代码如下所示
public final class Singleton {
public static final Singleton INSTANCE;
private Singleton() {
}
static {
Singleton var0 = new Singleton();
INSTANCE = var0;
}
}
可以看的出,反编译之后为饿汉式的方式实现单例的。
3.object单例模式的简单使用
简单使用:一般都用做工具类使用(object不能定义构造器)
示例如下
//声明单例类
object Singleton {
var age: Int = 0
var name: String = ""
fun test() {
println("test")
}
}
//使用单例类
fun main() {
Singleton.age = 10
Singleton.name = "zxf"
println(Singleton.name)
Singleton.test()
}
//输出
zxf
test
age:1
4.java访问object单例
那么java如何访问上述的单例类呢?
示例如下
Singleton.INSTANCE.getAge();
Singleton.INSTANCE.getName();
Singleton.INSTANCE.setName("zxf");
Singleton.INSTANCE.test();
上述是不是太麻烦了,有没有简单的方式呢?当然有,使用JvmField或者JvmStatic注解即可
示例如下
object Singleton {
//JvmField 对于java来说不生成getter/setter方法 访问等同于java Field
@JvmField
var age: Int = 0
var name: String = ""
//JvmField object的成员直接按照java静态成员生成字节码,对kotlin内部毫无影响,对于java可直接视为调用静态成员一般
@JvmStatic
fun test() {
println("test")
}
}
//java调用
Singleton.age = 10;
Singleton.test();
Singleton.INSTANCE.getName();
4.kotlin中对应java的静态成员
companion伴生对象,一般是 companion object 类似于 java静态变量方法的使用
示例如下
//java
public class Foo{
public static int age;
public static void y(){}
}
//kotlin 对应写法
class Foo{
companion object {
@JvmField
var age: Int = 0
@JvmStatic
fun y(){}
}
}
五、Kotlin内部类
1.Java与Kotlin实现内部类方式比较
模式:默认是静态内部类,加上inner 是非静态内部类
示例如下:
//java
public class Outer{
//非静态内部类,实例持有外部类实例引用
class Inner{}
//静态内部类
static class StaticInner{}
}
//kotlin
class Outer{
//非静态内部类,实例持有外部类实例引用
inner class Inner
//静态内部类
class StaticInner
}
2.Kotlin内名内部类
示例如下:
val runnable = object : Runnable {
override fun run() {
TODO("Not yet implemented")
}
}
kotlin匿名内部类:可以继承类或者实现多个接口
val value = object : Runnable, RemoteJVMLauncher.CallBack {
override fun run() {
TODO("Not yet implemented")
}
override fun jvmStarted() {
TODO("Not yet implemented")
}
}
六、Kotlin数据类data
1.Java与Kotlin实现内部类方式比较
数据类,不能够被继承,没有无参构造方法。可以进行解构操作、重新了equals、hashcode、toString、提供getter、setter、copy等函数
简单示例如下:
//java
public class Book{
private long id;
private String name;
private String person;
//省略getter/setter方法
}
//kotlin
data class Book(
var id:Long,
var name:String,
var person:String
)
//kotlin 解构的简单使用
val (id, name, person) = Book(1L, "zxf", "person")
println("id:$id,name:$name,person:$person")
//输出
id:1,name:zxf,person:person
2.data class 注意点
我们可以查看data class反编译之后的代码
public final class Book {
private final long id;
@NotNull
private final String name;
@NotNull
private final String person;
public final long getId() {
return this.id;
}
public final void setId(long var1) {
this.id = var1;
}
//其他getset省略
public Book(long id, @NotNull String name, @NotNull String person) {
Intrinsics.checkNotNullParameter(name, "name");
Intrinsics.checkNotNullParameter(person, "person");
super();
this.id = id;
this.name = name;
this.person = person;
}
public final long component1() {
return this.id;
}
@NotNull
public final String component2() {
return this.name;
}
@NotNull
public final String component3() {
return this.person;
}
@NotNull
public final Book copy(long id, @NotNull String name, @NotNull String person) {
Intrinsics.checkNotNullParameter(name, "name");
Intrinsics.checkNotNullParameter(person, "person");
return new Book(id, name, person);
}
// $FF: synthetic method
public static Book copy$default(Book var0, long var1, String var3, String var4, int var5, Object var6) {
if ((var5 & 1) != 0) {
var1 = var0.id;
}
if ((var5 & 2) != 0) {
var3 = var0.name;
}
if ((var5 & 4) != 0) {
var4 = var0.person;
}
return var0.copy(var1, var3, var4);
}
@NotNull
public String toString() {
return "Book(id=" + this.id + ", name=" + this.name + ", person=" + this.person + ")";
}
public int hashCode() {
//todo 省略
}
public boolean equals(@Nullable Object var1) {
//todo 省略
}
}
除了常规的方法外,还提供了几个component,上面我们使用的解构的功能,就是通过component实现的。
- 基于component提供相等性,解构等功能
- 数据类属性类型最好为基本类型、string、或者其他dataclass
- Component不可以自定义getter、setter
- 定义成员最好定义为val,防止修改后equals和hashcode的相等性判断错误
七、枚举类EnumClass
1.Java与Kotlin实现枚举类比较
java与kotlin实现枚举对比
简单示例如下:
//java
enum State{
InComplete,Complete
}
//使用
State.InComplete.name()//InComplete
State.InComplete.ordinal()//0
//kotlin
enum State{
InComplete,Complete
}
//使用
State.InComplete.name//InComplete
State.InComplete.ordinal//0
定义构造器示例如下:
//java
enum State{
InComplete(0),Complete(1);
int id;
State(int id){
this.id = id;
}
}
//kotlin
enum class State(val id: Int){
InComplete(0),Complete(1)
}
2.Kotlin枚举的简单使用
// 定义枚举类
enum class Color {
WHITE, BLACK, RED;
}
// 为枚举定义扩展方法
fun Color.nextColor(): Color {
// values()以集合的形式返回出所有的枚举
return Color.values().let {
// ordinal 当前的序号
val nextOrdinal = (this.ordinal + 1) % it.size
it[nextOrdinal]
}
}
fun main() {
// 获取枚举的位置和名字
val ordinal = Color.BLACK.ordinal
val name = Color.BLACK.name
println("enum:name:$name,index:$ordinal")
// 枚举比较大小 按照序号比较
println(Color.BLACK > Color.RED)
// 使用扩展方法获取下一个
println("whitenextcolor:${Color.WHITE.nextColor().name}")
// 枚举的区间
val closedRange = Color.WHITE..Color.RED
}
八、密封类sealed class
1.密封类概念
- 密封类是一种特殊的抽象类
- 密封类的子类定义在与自身相同的文件中
- 密封类的子类个数是有限的
密封类构造器是私有的,子类只能写在同一个文件夹里面
密封类构VS枚举类
密封类 | 枚举类 | |
---|---|---|
状态实现 | 子类继承 | 类实例化 |
状态可数 | 子类可数 | 实例可数 |
状态差异 | 类型差异 | 值差异 |
2.密封类简单使用
简单示例如下:状态机流转
// 密封类
sealed class PlayerState
// 定义子类
object Idle : PlayerState()
class Playing(val song: String) : PlayerState() {
fun stop() {}
fun start() {}
}
class Error(val song: String) : PlayerState() {
fun recover() {}
}
class Player(var state: PlayerState = Idle) {
fun play(song:String){
this.state = when(state){
Idle -> Playing(song).apply { this.start() }
is Playing -> {
(state as Playing).stop()
Playing(song).also { it.start() }
}
is Error ->{
(state as Error).recover()
Playing(song).apply { this.start() }
}
}
}
}
九、内联类inline
1.内联类概念
- 内联类是对某一类型的包装
- 内联类是类似java装箱类型的一种类型
- 编译器会尽可能使用被包装的类型进行优化
- 内联类在1.3中处于公测阶段,谨慎使用(内联类在1.5正式发布)
2.内联类定义
示例如下
//包装Int类型 必须是val
inline class BoxInt(val value:Int)
内联类可以实现接口,但不能继承父类也不能被继承
继承简单示例如下
inline class BoxInt(val value:Int):Comparable<Int>{
override fun compareTo(other:Int){
//todo
}
}
3.内联类的限制
- 主构造器必须有且仅有一个只读属性
- 不能定义有backing-field的其他属性
- 被包装类型必须不能是泛型类型
- 不能继承父类也不能被继承
- 内联类不能定义为其他类的内部类
typealias VS inline class
typealias | inline class | |
---|---|---|
类型 | 没有新类型 | 有包装类型产生 |
实例 | 与原类型一致 | 必要时使用包装类型 |
场景 | 类型更直观 | 优化包装类型性能 |
4.内联类使用例子
package org.example.zxf.kotlin5
// 内联类
inline class BoxInt(val value: Int) : Comparable<Int> {
override fun compareTo(other: Int): Int {
return value.compareTo(other)
}
operator fun inc(): BoxInt {
return BoxInt(value + 1)
}
}
// 使用内联类模拟枚举 减少开销
inline class EgEnum(val value: Int){
}
class Do {
companion object {
val ONE: EgEnum = EgEnum(0)
val TWO: EgEnum = EgEnum(1)
val THREE: EgEnum = EgEnum(2)
}
fun useEgEnum(other: EgEnum) {
println(other)
}
}
fun main() {
val boxInt = BoxInt(5)
if (boxInt < 10) {
println("value is less than 10")
}
val value = Do()
value.useEgEnum(Do.ONE)
}
十、尝试使用component实现解构
上面在将data class的时候,我们只有有一个解构的功能,和component有关,通过上面的源码,我们知道data class 就是通过component实现的解耦,那么我们能不能自己实现解耦呢?
当然是可以的,首先component是什么?
其实component也是运算符,我们可以看一下list扩展函数的component的实现。
val (x,y) = ArrayList<String>()
// 对应kotlin的源码
/**
* Returns 1st *element* from the list.
*
* Throws an [IndexOutOfBoundsException] if the size of this list is less than 1.
*/
@kotlin.internal.InlineOnly
public inline operator fun <T> List<T>.component1(): T {
return get(0)
}
/**
* Returns 2nd *element* from the list.
*
* Throws an [IndexOutOfBoundsException] if the size of this list is less than 2.
*/
@kotlin.internal.InlineOnly
public inline operator fun <T> List<T>.component2(): T {
return get(1)
}
可以看出其实就是将component作为运算符重载了,我们如果需要实现,只需要照葫芦画瓢就可以了。简单吧。
总结
以上便是Kotlin类型相关的东西了。下面可以协议demo。做一个简单的递归列表吧,可以指运行噢。
package org.example.zxf.kotlin5
// 密封类 是特殊的抽象类 可以被继承 只能在本文件内部使用
sealed class IntList {
/**
* 接口里面 fun test() 的方法默认为 public abstract 修饰的
* 抽象类里面 如果不屑abstract或者open 则默认不可以被重写
*
* 默认内部类为静态 用inner修饰的为非静态
* object 修饰饿汉式单例
*/
object Nil : IntList() {
override fun toString(): String {
return "NIL"
}
}
data class Cons(val head: Int, val tail: IntList) : IntList() {
override fun toString(): String {
return """(head:$head,tail:$tail)"""
}
}
fun intJoinToString(sep: Char): String {
return when (this) {
Nil -> "NIL"
is Cons -> {
// this 可以访问 因为编译器自动做了类型转换 转换为了Cons
"${this.head}${sep}${this.tail.intJoinToString(sep)}"
}
}
}
}
// 求和
fun IntList.sum(): Int {
return when (this) {
IntList.Nil -> 0
is IntList.Cons -> {
this.head + this.tail.sum()
}
}
}
// 定义解构
operator fun IntList.component1(): Int {
return when (this) {
IntList.Nil -> 0
is IntList.Cons -> this.head
}
}
operator fun IntList.component2(): Int {
return when (this) {
IntList.Nil -> 0
is IntList.Cons -> tail.component1()
}
}
operator fun IntList.component3(): Int {
return when (this) {
IntList.Nil -> 0
is IntList.Cons -> tail.component2()
}
}
fun initListOf(vararg arg: Int): IntList {
return when (arg.size) {
/**
* slice 获取 range内的数据 返回List
*
* '*'可以将array分割成一个一个 传给可变长参数
*/
0 -> IntList.Nil
else -> IntList.Cons(arg[0], initListOf(*(arg.slice(1 until arg.size).toIntArray())))
}
}
// 递归列表实现
//[0,1,2,3]
fun main() {
// 手动初始化
val cons = IntList.Cons(0, IntList.Cons(1, IntList.Cons(2, IntList.Cons(3, IntList.Nil))))
println(cons.toString())
// 使用initListOf 初始化
val conss = initListOf(0, 1, 2, 3)
println(conss.toString())
println(conss.intJoinToString('+'))
println("sum:${conss.sum()}")
val (a, s, d) = conss
println("a:$a,s:$s,d:$d")
}