三、Kotlin 类型初步

1. 类 & 接口

1.1 类的定义

1.1.1 空类的定义

Java 的定义:

public class Foo {}

Kotlin 的定义:

class Foo

注意:

  1. 类的访问权限修饰符默认为 public

  2. 若类的 {} 为空,可以省略不写。

1.1.2 带成员的类的定义

Java 中定义类:

public class Foo {
    public int x;
    public void methodName() {}
}

Kotlin 中定义类:

class Foo {
    val x: Int = 0  // 需要显示初始化
    fun methodName() {}
}

注意:

Java 中定义类中的成员属性时,不需要显示初始化,会有一个默认值。

但是,Kotlin 中定义成员属性时,需要显示初始化。

1.1.3 带有参构造器的类的定义(最终版本:在主构造器上声明成员变量)

Java 中定义类:

public class Foo {
    public int x;

    public Foo(int x) {
        this.x = x;
    }
}

Kotlin 中定义类:

class Foo {
    val x: Int

    constructor(x: Int) { // 副构造器(secondary constructor)
        this.x = x
    }
}

还可以写成:

class Foo constructor(x: Int) { // 主构造器(primary constructor)
    val x: Int = x
}

还可以简化成:

class Foo(x: Int) { // 主构造器(primary constructor)
    val x: Int = x
}

最终版本:

class Foo(var x: Int) { // 主构造器(primary constructor)

}

注意:

  1. 如最终版本所示,可以将成员属性定义在主构造器中。此时,在调用主构造器时,会为成员属性初始化。

  2. var 定义的变量是可读写变量;val 定义的变量是只读变量。

举例:

在这里插入图片描述

1.2 类的实例化(省略 new 关键字)

Java 中实例化对象:

public class Foo {}
Foo foo = new Foo();

Kotlin 中实例对象:

class Foo
val foo = Foo()

注意:Kotlin 中实例化对象时不需要使用 new 关键字。

1.3 接口的定义

Java 中的接口定义:

public interface IFoo {
    void methodName();
}

Kotlin 中的接口定义:

interface IFoo {
    fun methodName()  // 无返回值时 ": Unit" 可省略
}

注意:Kotlin 中的分号 “;” 可省略。

1.4 接口的实现(关键字 override 不可以省略)

Java 中的接口实现:

public interface IFoo {
    void methodName();
}

public class implements IFoo {
    @Override
    public void methodName() {}
}

Kotlin 中的接口实现:

interface IFoo {
    fun methodName()
}

class Foo: IFoo {
    override fun methodName() {} // 关键字 override 不可以省略
}

注意:

  1. Kotlin 中大量使用了冒号 “:

  2. 重写方法时,Java 中的注解 @Override 是可以省略的。但是,Kotlin 中的关键字 override 不可以省略。

1.5 抽象类的定义

Java 中抽象类的定义:

public abstract class AbsFoo {
    public abstract void absMethodName();
    protected void methodName1() {}
    public final void methodName2 {}
}

注意:

Java 的抽象类中,除抽象方法外的普通方法,默认是可以被子类重写的。

如果不想被子类重写,需要用关键字 final 修饰。

Kotlin 中抽象类的定义:

abstract class AbsFoo {
    abstract void absMethodName()
    open fun methodName1() {}
    fun methodName2() {}
}

注意:

Kotlin 的抽象类中,除抽象方法外的普通方法,默认是不可以被子类重写的。

如果想被子类重写,需要用关键字 open 修饰。

1.6 类的继承

Java 中的类继承:

public interface IFoo {
    ...
}

public abstract class AbsFoo {
    ...
}

public class FooImpl extends AbsFoo implements IFoo {
    ...
}

Kotlin 中的类继承:

interface IFoo {
    ...
}

abstract class AbsFoo {
    ...
}

class FooImpl(var x: Int): AbsFoo(), IFoo { // 继承类 AbsFoo 时,指定调用 AbsFoo 的无参构造方法
    ...
}

注意:

  1. Kotlin 中定义类时,成员属性可以在主构造器中定义。

  2. Kotlin 中继承父类时,需要指定调用父类的哪个构造器。

1.7 关键字 open

1.7.1 非抽象方法不加 open 无法重写 & 非抽象类不加 open 无法继承

对于父类中的非抽象方法那样,如果不加关键字 open,子类无法重写。

类似地,对于非抽象类,如果不加关键字 open,也是无法被继承的。

1.7.2 关键字 open 可以被继承

关键字 open 是可以被继承的。

即:子类重写了父类的被 open 修饰的非抽象方法后,子类中的这个非抽象方法也默认被 open 修饰。

此时,如果子类不想再让下一级子类重写这个非抽象方法,则需要使用关键字 final 修饰。

在这里插入图片描述

1.8 Kotlin 中的成员属性:Property = Field + setter/getter

类中的成员包括:成员方法和成员变量。其中,成员变量又可以称为成员属性(简称 属性)。

Java 中,定义一个成员属性,就仅仅是定义了一个字段(Field)而已,并且标准的做法是:

  1. 将该字段设置为 private
  2. 并提供 setter/getter 方法。

Kotlin 中,定义一个成员属性,就相当于 Java 中的 Field + setter/getter,即:

Kotlin 中的成员属性 Property = Field + setter/getter
1.8.1 成员属性的定义

Java 中:

public class Person {
    private String name;
    private int age;

    public void setName(String name) {
        this.name = name
    }

    public String getName() {
        return name
    }
    ...
}

Kotlin 中:

class Person(name: String, age: Int) {

    var name: String = name
    get() {
        return field
    }
    set(value) {
        field = value
    }
}

Kotlin 中定义一个成员属性后,会默认提供 setter/getter 方法。当然,也可以像上面这样重写成员属性的 setter/getter 方法。

需要注意的是:

  1. 重写的 setter/getter 方法必须紧跟在成员属性定义之后。所以,此时不能在主构造器中定义成员属性。

  2. setter/getter 方法中的 field 是固定写法,表示成员属性本身。

1.8.2 属性引用(属性也是一种类型)

Kotlin 中的属性(Property)= 字段(Field)+ setter/getter

Kotlin 中的 属性也是一种类型。同将函数引用赋给函数类型的变量一样,可以将属性引用赋给属性类型的变量。

于是,我们 可以通过属性类型的变量调用属性的 setter/getter 方法

class Foo(name: String) {
    var name: String = name
    get() {
        return field
    }
    set(value) {
        field = value
    }
}

val foo = Foo("xiaoming")

// 通过类名进行的属性引用
val nameRef1 = Foo::name  
nameRef1.get(foo)
nameRef1.set(foo, "zhangsan") // 需要指定具体对象

// 通过对象名进行的属性引用
val nameRef2 = foo::name  
nameRef2.get()
nameRef2.set("zhangsan")

在这里插入图片描述

1.8.3 Koltin 接口中定义成员属性(接口中定义的属性,子类必须重写)

Kotlin 的接口中可以定义成员属性。且 接口中定义的属性,子类必须重写

在这里插入图片描述

1.9 类的扩展成员

Kotlin 中,在定义了一个类之后,还可以在类外为该类添加 扩展方法扩展属性

1.9.1 扩展方法
1.9.1.1 扩展方法的定义
//定义类
class Foo 

//定义扩展方法
fun Foo.extFunName(p1: T1, p2: T2, ...): ReturnType {}
1.9.1.2 扩展方法的类型

扩展方法的类型跟成员方法是一样的:

1. 对于 Foo::extFunName,函数类型为 (Foo, T1, T2, ...) -> ReturnType
2. 对于 foo::extFunName,函数类型为 (T1, T2, ...) -> ReturnType
1.9.2 扩展属性
1.9.2.1 扩展属性的定义:
//定义类
clas Foo {
    var attr: String = ""
}

//定义扩展属性
var Foo.extAttr: String
get() = attr  // 相当于 get() {return attr}
set(value) {attr = value}
1.9.2.2 扩展属性没有 backing field(接口中定义的属性也没有)

扩展属性没有 field(即 backing field),所以无法保存状态。只能绑定其他的成员属性来实现状态的保存。

在这里插入图片描述

注意:除了类的扩展属性没有 backing field,无法保存状态之外。接口中定义的成员属性也没有 backing field,也无法保存状态。

在这里插入图片描述

2. 空类型安全

2.1 可空类型(Type?)& 不可空类型(Type

Kotlin 中的引用类型 默认都是不可空类型

不可空类型的变量永远不可能为 null

如果想使引用类型变成可空类型,则需要在引用类型后面加问号 “?

不可空类型:

var x: Type = initValue // initValue 不可以是 null

可空类型:

var x: Type? = null // 可空类型的变量可以是 null

在这里插入图片描述

2.2 可空类型强转为不可空类型(obj!!

对于可空类型 Type? 的对象 obj

  1. 可以通过 “obj!!” 强转为不可空类型 Type

  2. 可以通过 “obj!!.member” 强转为不可空类型后再访问成员

注意:只有当我们能够确定可空类型的变量不为 null,才能将其强转为不可空类型

在这里插入图片描述

2.3 可空类型的对象访问成员

2.3.1 通过 “?.” 返回可空类型的结果

可空类型 Type? 的对象 obj 访问成员时,需要加 “?” ,此时返回的结果也是可空类型 Type?

val ret = obj?.member  // ret 是可空类型 Type?
2.3.2 结合使用 elvis 运算符 “?:” 得到不可空类型的结果

若想得到一个不可空类型 Type 的结果,需要结合使用 elvis 运算符 “?:

elvis 运算符的语法:

leftExpress ?: rightExpress

说明:
1. 当 leftExpress 的结果不为空时,运算符 "?:" 的结果为 leftExpress 的结果
2. 当 leftExpress 的结果为空时,运算符 "?:" 的结果为 rightExpress 的结果

举例:

// obj 为空时,返回 defValue;若 defValue 不为空,则结果 ret 是不可空类型 Type
val ret = obj?.member?:defValue  

在这里插入图片描述

2.4 不可空类型(Type)是可空类型(Type?)的子类

通过里式替换原则进行验证可知:不可空类型 Type 是可空类型 Type? 的子类。

里式替换原则的定义:所有引用过基类的地方必须能透明地使用其子类的对象。

在这里插入图片描述

2.5 平台类型(Type!

2.5.1 什么是平台类型

Kotlin 代码不仅可以编译成 Java 代码,还可以编译成 JavaScript 代码,或 Native 代码(c/c++

以编译成 Java 为例:

  1. Kotlin 代码可以兼容 Java 代码。

  2. Kotlin 中访问 Java 类时,Java 中的类类型 Type 就是一个 Java 平台类型(Kotlin 中表示为 Type!

2.5.2 @NonNull/Type & @Nullable/Type? & Type!
import androidx.annotation.Nullable;
public class Person {
    private String name;

    public String getName1() {
        return name;
    }

    @NonNull
    public String getName2() {
        return name;
    }

    @Nullable
    public String getName3() {
        return name;
    }
}

如上代码所示,在 Kotlin 代码中访问 JavaPerson 时:

  1. 当访问用 @NonNull 修饰的方法时,方法的返回值类型是不可空类型 Type

  2. 当访问用 @Nullable 修饰的方法时,方法的返回值类型是可空类型 Type?

  3. 当访问无注解修饰的方法时,方法的返回值类型是平台类型 Type!

2.5.3 无法主动声明变量为平台类型

平台类型 Type!Kotlin 内部使用,程序员无法手动将变量声明为平台类型 Type!

2.5.4 平台类型的对象访问成员(无法确定平台类型的变量是否为 null

当对象为平台类型 Type! 时,无法确定该对象是否为空

2.5.4.1 通过 “?.” 返回可空类型的结果

可以使用运算符 “?.” 来访问平台类型的对象的成员,得到的结果是可空类型 Type?

2.5.4.2 结合 elvis 运算符 “?:” 得到不可空类型的结果

也可以结合运算符 “?.” 和 elvis 运算符 “?:” 来访问成员。当对象为空时,赋以默认结果值,从而得到一个不可空类型 Type 的结果。

在这里插入图片描述

2.6 运算符 “!!.”、“?.”、“?:” 使用总结

  1. 若对象 obj 是可空类型 Type,且能肯定 obj 非空,则可以使用 “!!.” 访问成员,得到的结果是不可空类型 Type

    val ret = obj!!.member  // ret 是不可空类型 Type
    
  2. 若对象 obj 是可空类型 Type?,且无法确定 obj 是否为空,则可以使用 “?.” 访问成员,得到的结果是可空类型 Type?

    val ret = obj?.member  // ret 是可空类型 Type?
    
  3. 若对象 obj 是可空类型 Type?,且无法确定 obj 是否为空,但是想访问成员得到一个非空的结果,则可以结合使用 “?.” 和 “?:”,使得 obj 为空时返回一个默认的非空结果值,此时得到的结果是不可空类型 Type

    val ret = obj?.member?:defValue  // defValue 非空,ret 是不可空类型 Type
    
  4. 特别地,当对象 obj 是平台类型 Type! 时, 也可以结合使用 “?.” 和 “?:” 得到一个不可空类型的结果。

3. 智能类型转换

3.1 父类引用转子类引用:if(obj is Foo)

Java 中的类型转换如下:

/* 接口定义 */
public interface IPerson {}

/* 类定义 */
public class Person implements IPerson {
    private String name;
    public Person(String name) { this.name = name; }
        
    public String getName() {return name;}
}

/* 转换 */
IPerson p = new Person("xiaoming");
if(p instanceof Person) {
    String name = ((Person)p).getName()
}

Kotlin 中的类型转换如下:

/* 接口定义 */
interface IPerson

/* 类定义 */
class Person(var name: String): IPerson

/* 转换 */

val p: IPerson = Person("xiaoming")

// is 表示判断变量 p 是否是 Person类型(相当于 Java 中的 instanceof)
if(p is Person) { 
    /*
        as 表示将变量 p 转为 Person 类型
        根据 if 判断语句,为 true 时说明变量 p 就是 Person 类型了,
        所以这里可以省略 as 转换,Kotlin 内部会进行智能类型转换。
    */
    // val name = (p as Person).name

    val name = p.name // 智能类型转换为 Person 类型
}

3.2 可空类型转不可空类型:if(obj != null)(只适用于局部变量)

对于局部变量 obj,若其类型为可空类型 Type,则可以通过 if 语句进行非空判断,此时在 if 语句的代码块中可空类型变量 obj 会被 Kotlin 智能转换为不可空类型变量。

在这里插入图片描述

4. 类型的安全转换:obj as? Foo

当可以保证变量 obj 能够转换为 Type 时,可以使用 as 将变量 obj 转为不可空类型 Type

当无法保证变量 obj 能够转换为 Type 时,应该使用 as? 进行转换,转换后的结果是可空类型 Type?

如果转换失败,那么 as? 返回 nullas? 称为 类型的安全转换

在这里插入图片描述

5. 编程建议

5.1 尽量使用 val 声明不可变引用

尽可能使用 val 声明不可变引用,使程序代码的含义更加清晰明确。

5.2 尽量减少函数对全局变量的直接访问(可以通过局部变量访问)

尽可能减少函数对全局变量的访问(多线程访问全局变量时容易产生线程同步的问题),这也为函数式编程提供了基础。

必要时创建局部变量来指向全局变量,避免因对全部变量的多线程访问而造成的问题。

5. 案例:使用 Retrofit 发送网络请求

5.1 添加依赖:

dependencies {
    ...
    implementation 'com.squareup.retrofit2:retrofit:2.6.2'
    implementation 'com.squareup.retrofit2:converter-gson:2.6.2'
    implementation 'com.google.code.gson:gson:2.8.1'
}

5.2 注意事项

  1. data class” 声明一个数据类(后面课程会有介绍)。

  2. obj::class 返回 Kotlin 的字节码对象。

  3. obj::class.java 返回 Java 的字节码对象。

    其中 javaKotlin 的字节码类型 KClass 的一个扩展属性。

  4. Kotlin 中,为 Javajava.io.File 提供了大量的扩展方法,从而使得在 Kotlin 中可以方便地进行 IO 操作,并且不需要考虑 IO 流的释放关闭。

5.3 代码示例(File.writeText & data class & obj::class.java

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/348378.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

普通人如何打造自己人生的护城河?

哈喽,大家好啊,我是雷工。 今天在看《张一鸣管理日志》时看到这么一句话: 今日头条不断吸引更优秀的工程师,不断更新算法,才有了当前今日头条的算法护城河。 头条的算法有多牛,看你周边就知道了。 越来越多…

《剑指 Offer》专项突破版 - 面试题 28 : 展平多级双向链表(C++ 实现)

题目连接:LCR 028. 扁平化多级双向链表 - 力扣(LeetCode) 题目: 在一个多级双向链表中,节点除了有两个指针分别指向前后两个节点,还有一个指针指向它的子链表,并且子链表也是一个双向链表&…

CentOS7自动备份数据库到git

虽然数据库没什么数据,但是有就是珍贵的啦,为了服务器什么的无了,所以还是要自动备份一下比较好。 Open备忘第一页 步骤 在Gitee(github)上创建一个私有仓库Gitee(github)配置好服务器的ssh在服…

获取双异步返回值时,如何保证主线程不阻塞?

目录 一、前情提要二、JDK8的CompletableFuture1、ForkJoinPool2、从ForkJoinPool和ThreadPoolExecutor探索CompletableFuture和Future的区别 三、通过CompletableFuture优化 “通过Future获取异步返回值”1、通过Future获取异步返回值关键代码(1)将异步…

k8s的图形化工具--rancher

什么是rancher? rancher是一个开源的企业级多集群的k8s管理平台 rancher和k8s的区别 都是为了容器的调度和编排系统,但是rancher不仅能够调度,还能管理k8s集群,自带监控(普罗米修斯) 实验部署 实验架构…

大语言模型推理提速:TensorRT-LLM 高性能推理实践

作者:顾静 TensorRT-LLM 如何提升 LLM 模型推理效率 大型语言模型(Large language models,LLM)是基于大量数据进行预训练的超大型深度学习模型。底层转换器是一组神经网络,这些神经网络由具有 self-attention 的编码器和解码器组…

基于Java+SpringMvc+vue+element实现上海汽车博物馆平台

基于JavaSpringMvcvueelement实现上海汽车博物馆平台 🍅 作者主页 央顺技术团队 🍅 欢迎点赞 👍 收藏 ⭐留言 📝 🍅 文末获取源码联系方式 📝 🍅 查看下方微信号获取联系方式 承接各种定制系统 …

《Visual Tree Convolutional Neural Network in Image Classification》阅读笔记

论文标题 《Visual Tree Convolutional Neural Network in Image Classification》 图像分类中的视觉树卷积神经网络 作者 Yuntao Liu、Yong Dou、Ruochun Jin 和 Peng Qiao 来自国防科技大学并行和分布式处理国家实验室 初读 摘要 问题: 在图像分类领域&…

JCL中常用的DD语句

JCL中的DD语句介绍 ​ DD语句,主要定义数据集用的,也叫做DATASET DEFINE,分为定义设备的UNIT、VOLUME、SPACE,定义数据集的DSN、DISP、DCB,详细可以看英文版的《MVS JCL Reference》,还有一些特殊的DD,暂时…

hdu1195 Open the lock 双向广度优先搜索

D-BFS 双向广度优先搜索 从起点和终点同时开始搜索&#xff0c;直到两个搜索的点相交&#xff0c;得到最短路径 Code: // D-BFS //by:MuQY #include <iostream> #include <algorithm> #include <string.h> #include <queue> #include <string> …

redis + 拦截器 :防止数据重复提交

1.项目用到,不是核心 我们干系统开发,不免要考虑一个点&#xff0c;数据的重复提交。 我想我们之前如果要校验数据重复提交要求&#xff0c;会怎么干?会在业务层&#xff0c;对数据库操作&#xff0c;查询数据是否存在,存在就禁止插入数据; 但是吧,我们每次crud操作都会连接…

Conda 使用environment.yml创建一个新的Python项目

Conda系列&#xff1a; 翻译: Anaconda 与 miniconda的区别Miniconda介绍以及安装Conda python运行的包和环境管理 入门Conda python管理环境environments 一 从入门到精通Conda python管理环境environments 二 从入门到精通Conda python管理环境environments 三 从入门到精通…

luceda ipkiss教程 58:输出器件的版图和三维模型

在ipkiss中&#xff0c;通过visualize_3d_povray可以输出包含器件的三维模型参数的.pov文件&#xff0c;再通过POV-Ray&#xff08;免费软件&#xff0c;下载地址&#xff1a;https://www.povray.org/download/&#xff09;就可以查看器件的三维模型。 如&#xff1a; 代码如…

账号定位基础

1.账号定位流程 定位体系 &#xff08;1&#xff09;商业定位 ①商业定位-带货 ②商业定位-引流 ③商业定位-接广告 ④商业定位-直播打赏 &#xff08;2&#xff09;内容定位 内容定位分为&#xff1a;主题IP、人物IP &#xff08;2-1&#xff09;主题IP ①有用处 ②有兴…

144基于matlab的平面桁架结构的总体刚度矩阵计算

基于matlab的平面桁架结构的总体刚度矩阵计算&#xff0c;最后以图形形式显示出桁架结构&#xff0c;程序已调通&#xff0c;可直接运行。 144matlab 平面桁架 有限元分析 总体刚度 (xiaohongshu.com)

0125-2-Vue深入学习1—mustache模板引擎原理

[mustache] 是 “胡子”的意思&#xff0c;因为它的嵌入标记 {{ }} 旋转过来很像[胡子]&#xff0c;Vue中的 {{ }} 语法也引用了mustache&#xff0c;这也是我深入学习的目的。 1、原始js方式使 数据 变为视图 <ul id"list"></ul><script>var arr …

Git常用命令介绍

Git 是一个开源的分布式版本控制系统&#xff0c;用于敏捷高效地处理任何或小或大的项目 一、Git的安装 安装包下载地址&#xff1a;https://gitforwindows.org/ 国内的镜像地址&#xff1a;https://npm.taobao.org/mirrors/git-for-windows/ 完成安装之后&#xff0c;在开…

面试常问的Spring AOP底层原理

AOP底层原理可以划分成四个阶段&#xff1a;创建代理对象阶段、拦截目标对象阶段、调用代理对象阶段、调用目标对象阶段 第一阶段&#xff1a;创建代理对象阶段 通过getBean&#xff08;&#xff09;方法创建Bean实例根据AOP的配置匹配目标类的类名&#xff0c;判断是否满足切…

wfuzz网站模糊测试

https://github.com/xmendez/wfuzz Wfuzz: The Web fuzzer — Wfuzz 2.1.4 documentatio n 一、wfuzz介绍 WFuzz是基于Python开发的 Web安全模糊测试工具。可以将其理解为Fuzz一款暴力破解工具。根据用户提供的字典&#xff0c;获取web站点的敏感目录和信息。 Wfuzz 提供了一个…

小程序样例3:根据日历创建待办事项

基本功能 1、待办事项查看 选择不同的日期显示不同的待办: 2、选择日期后 新增事项&#xff1a; 3. 点击事项&#xff0c;查看详情 4、删除事项&#xff1a;删除事项3之后&#xff0c;剩余事项2 5、点击日期可以选择更多的月&#xff1a; 实现思路&#xff1a; 1、数据结构&a…