Kotlin学习(五):kotlin类详解进阶

  • 4.java访问object单例

  • 4.kotlin中对应java的静态成员

  • 五、Kotlin内部类

    • 1.Java与Kotlin实现内部类方式比较
  • 2.Kotlin内名内部类

  • 六、Kotlin数据类data

    • 1.Java与Kotlin实现内部类方式比较
  • 2.data class 注意点

  • 七、枚举类EnumClass

    • 1.Java与Kotlin实现枚举类比较
  • 2.Kotlin枚举的简单使用

  • 八、密封类sealed class

    • 1.密封类概念
  • 2.密封类简单使用

  • 九、内联类inline

    • 1.内联类概念
  • 2.内联类定义

  • 3.内联类的限制

  • 4.内联类使用例子

  • 十、尝试使用component实现解构

  • 总结

一、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 任务中一次调用 文件

下面针对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 getValue(thisRef: Any?, property: KProperty<*>): T{

}

operator fun 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: i d , n a m e : id,name: 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() {

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
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() {

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

[外链图片转存中…(img-rqMRaCGv-1715680694361)]

[外链图片转存中…(img-c9ogWIAG-1715680694362)]

[外链图片转存中…(img-yNbUeX6d-1715680694363)]

[外链图片转存中…(img-glMQxFrM-1715680694364)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 29
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值