Kotlin教程笔记(18) - 数据类

简介: Kotlin教程笔记(18) - 数据类

本系列学习教程笔记属于详细讲解Kotlin语法的教程,需要快速学习Kotlin语法的小伙伴可以查看“简洁” 系列的教程

快速入门请阅读如下简洁教程:
Kotlin学习教程(一)
Kotlin学习教程(二)
Kotlin学习教程(三)
Kotlin学习教程(四)
Kotlin学习教程(五)
Kotlin学习教程(六)
Kotlin学习教程(七)
Kotlin学习教程(八)
Kotlin学习教程(九)
Kotlin学习教程(十)

Kotlin教程笔记(18) - 数据类

imgKotlin - 数据类

#componentN 方法

大前端 es6 中有一个解构的语法特性我很是喜欢,通过类似 let {name,isCheck} = option 的写法就可以从 option 对象中提取出 name 和 isCheck 属性,而不再是使用 let name = option.name 这种写法,解构的语法让代码得以更进一步的简化,那么作为后起之秀的 Kotlin 是否也有这种特性呢?没错,Kotlin 也有(注意:两者还是有区别的),它就是 componentN 方法 ,我们直接来看代码:

class User(val username: String, val age: Int) {
    operator fun component1(): String {
        return username
    }

    operator fun component2(): Int {
        return age
    }
}

fun main() {
    val user = User("lqr", 18)
    println("username = ${user.username} , age = ${user.age}") // username = lqr , age = 18

    val (username, age) = user
    println("username = $username , age = $age") // username = lqr , age = 18

    println(user) // com.charylin.kotlinlearn.User@2f0e140b
}

使用 val (arg1, arg2) in object 这种写法,就可以从 object 对象中提取出 component1() 和 component2() 的返回结果,而且 componentN 方法 的返回值类型是没有限制的。

es6 的解构语法是根据变量名提取属性值的,而 Kotlin 的 componentN 方法 则是按顺序调用 componentN() 给变量赋值,所以这两者还是有差别的!!!

#数据类(data class)

你可能会觉得 Kotlin 为了达到类似 "es6 解构" 的语法特性,对开发者的要求有点高啊,还要自己编写 componentN 方法 ,是的,如果就只是这样的话,那很糗唉,还好,Kotlin 中的 数据类(data class) 就可以自动为对象生成 componentN 方法 ,不仅如此,data class 还会默认生成 toString()copy() 等方法:

data class User(val username: String, val age: Int)

fun main() {
    val user = User("lqr", 18)
    println("username = ${user.username} , age = ${user.age}") // username = lqr , age = 18

    val (username, age) = user
    println("username = $username , age = $age") // username = lqr , age = 18

    println(user) // User(username=lqr, age=18)
}

可见,使用 data class 就无须自己手写 componentN 方法 了,抛开 "es6 解构" 语法,我们来看 数据类(data class) ,顾名思义, data class 被设计出来就是为了存储数据的,最核心最应该被关注的就是数据本身,所以在声明 data class 时,我们只需要把成员属性写好,剩下的交给 data class ,它会自己处理好 toString()copy() 等方法,componentN 方法 也只是为了方便开发者提取其中的数据而已。

从 Java 开发者的角度来看,数据类(data class) 的诞生就是为了取代 JavaBean 的。

还记得 IntArray.withIndex() 吗?它的返回值 Iterable<IndexedValue<Int>> 中泛型类 IndexedValue 就是 data class

// _Arrays.kt
/**
 * Returns a lazy [Iterable] of [IndexedValue] for each element of the original array.
 */
public fun IntArray.withIndex(): Iterable<IndexedValue<Int>> {
    return IndexingIterable { iterator() }
}

// IndexedValue.kt
/**
 * Data class representing a value from a collection or sequence, along with its index in that collection or sequence.
 *
 * @property value the underlying value.
 * @property index the index of the value in the collection or sequence.
 */
public data class IndexedValue<out T>(public val index: Int, public val value: T)

因此,在 for 循环中,才会有 (index, value) in intArray.withIndex() 这种写法,本质上就是使用了 data class 默认生成的 componentN 方法

val intArray = intArrayOf(1, 2, 3, 4, 5)
for ((index, value) in intArray.withIndex()) {
    println("index = $index, value = $value")
}

#allOpen 和 noArg 插件

相比 JavaBean,虽然 data class 有很多好用的语法糖特性,但也有其不足的地方:

  • data class 默认 final,无法被继承。
  • data class 默认没有无参构造函数。

借助 Show Kotlin Bytecode 工具,将上面的 data class User 类反编译成 Java 代码,发现确实有这 2 点问题:

public final class User {
   
   @NotNull
   private final String username;
   private final int age;

   @NotNull
   public final String getUsername() {
   
      return this.username;
   }

   public final int getAge() {
   
      return this.age;
   }

   public User(@NotNull String username, int age) {
   
      super();
      this.username = username;
      this.age = age;
   }

   @NotNull
   public final String component1() {
   
      return this.username;
   }

   public final int component2() {
   
      return this.age;
   }

   @NotNull
   public final User copy(@NotNull String username, int age) {
   
      Intrinsics.checkParameterIsNotNull(username, "username");
      return new User(username, age);
   }

   @NotNull
   public String toString() {
   
      return "User(username=" + this.username + ", age=" + this.age + ")";
   }

   public int hashCode() {
    ... }

   public boolean equals(@Nullable Object var1) {
    ... }
}

在一些开发场景下(如数据库),会要求数据类必须要有无参构造函数或可继承,这对 data class 本身来说是无解的,但可以借助 allOpennoArg 插件来解决这个问题,让 data class 在编译期增加无参构造器(noArg 插件),并去除 final 限制(allOpen 插件),集成这 2 个插件的步骤如下:

#1. 声明注解类

编写一个注解类,名字随便,后续用得到:

annotation class Poko

#2. 配置 Gradle 脚本

在工程的 gradle 脚本文件中,配置 allOpen 和 noArg 插件:

buildscript {
   
    ext.kotlin_version = '1.3.72'
    repositories {
   
        mavenCentral()
    }
    dependencies {
   
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        // Step1. 依赖插件
        classpath "org.jetbrains.kotlin:kotlin-noarg:$kotlin_version"
        classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
    }
}

// Step2. 应用插件
apply plugin: "kotlin-noarg"
apply plugin: "kotlin-allopen"

// Step3. 配置插件
noArg {
   
    // invokeInitializers = true // ture:无参构造器中执行 init{} 代码;false(默认值):不执行 init{} 代码
    annotation("com.charylin.kotlinlearn.annotation.Poko")
}
allOpen {
   
    annotation("com.charylin.kotlinlearn.annotation.Poko")
}

Gradle 脚本中,noArgallOpen 配置项就用到了前面的注解类 Poko,只需要把注解类 Poko 的全路径配置进去就好了。

#3.使用注解类

data class 上使用注解类 Poko :

@Poko
data class User(val username: String, val age: Int)

经过上述步骤便完成了 allOpennoArg 插件的集成和使用,接下来就是 rebuild project,然后再反编译看看 User 类的 Java 代码:

public class User {
   
   ...
   public User(@NotNull String username, int age) {
   
      Intrinsics.checkParameterIsNotNull(username, "username");
      super();
      this.username = username;
      this.age = age;
   }

   public User() {
   
   }
}

可以看到,final 限制去除了,也多了一个无参构造器。前面也提到了,这 2 个插件是在编译期对 data class 进行修改,也就是说,我们无法在编译期之前(也就是 coding 的时候)去使用该无参构造器或继承该 data class,不过呢,可以通过反射来处理这个问题。

目录
相关文章
|
3天前
|
存储 人工智能 弹性计算
阿里云弹性计算_加速计算专场精华概览 | 2024云栖大会回顾
2024年9月19-21日,2024云栖大会在杭州云栖小镇举行,阿里云智能集团资深技术专家、异构计算产品技术负责人王超等多位产品、技术专家,共同带来了题为《AI Infra的前沿技术与应用实践》的专场session。本次专场重点介绍了阿里云AI Infra 产品架构与技术能力,及用户如何使用阿里云灵骏产品进行AI大模型开发、训练和应用。围绕当下大模型训练和推理的技术难点,专家们分享了如何在阿里云上实现稳定、高效、经济的大模型训练,并通过多个客户案例展示了云上大模型训练的显著优势。
|
7天前
|
存储 人工智能 调度
阿里云吴结生:高性能计算持续创新,响应数据+AI时代的多元化负载需求
在数字化转型的大潮中,每家公司都在积极探索如何利用数据驱动业务增长,而AI技术的快速发展更是加速了这一进程。
|
4天前
|
人工智能 运维 双11
2024阿里云双十一云资源购买指南(纯客观,无广)
2024年双十一,阿里云推出多项重磅优惠,特别针对新迁入云的企业和初创公司提供丰厚补贴。其中,36元一年的轻量应用服务器、1.95元/小时的16核60GB A10卡以及1元购域名等产品尤为值得关注。这些产品不仅价格亲民,还提供了丰富的功能和服务,非常适合个人开发者、学生及中小企业快速上手和部署应用。
|
12天前
|
人工智能 弹性计算 文字识别
基于阿里云文档智能和RAG快速构建企业"第二大脑"
在数字化转型的背景下,企业面临海量文档管理的挑战。传统的文档管理方式效率低下,难以满足业务需求。阿里云推出的文档智能(Document Mind)与检索增强生成(RAG)技术,通过自动化解析和智能检索,极大地提升了文档管理的效率和信息利用的价值。本文介绍了如何利用阿里云的解决方案,快速构建企业专属的“第二大脑”,助力企业在竞争中占据优势。
|
14天前
|
自然语言处理 数据可视化 前端开发
从数据提取到管理:合合信息的智能文档处理全方位解析【合合信息智能文档处理百宝箱】
合合信息的智能文档处理“百宝箱”涵盖文档解析、向量化模型、测评工具等,解决了复杂文档解析、大模型问答幻觉、文档解析效果评估、知识库搭建、多语言文档翻译等问题。通过可视化解析工具 TextIn ParseX、向量化模型 acge-embedding 和文档解析测评工具 markdown_tester,百宝箱提升了文档处理的效率和精确度,适用于多种文档格式和语言环境,助力企业实现高效的信息管理和业务支持。
3935 2
从数据提取到管理:合合信息的智能文档处理全方位解析【合合信息智能文档处理百宝箱】
|
3天前
|
算法 安全 网络安全
阿里云SSL证书双11精选,WoSign SSL国产证书优惠
2024阿里云11.11金秋云创季活动火热进行中,活动月期间(2024年11月01日至11月30日)通过折扣、叠加优惠券等多种方式,阿里云WoSign SSL证书实现优惠价格新低,DV SSL证书220元/年起,助力中小企业轻松实现HTTPS加密,保障数据传输安全。
496 3
阿里云SSL证书双11精选,WoSign SSL国产证书优惠
|
10天前
|
安全 数据建模 网络安全
2024阿里云双11,WoSign SSL证书优惠券使用攻略
2024阿里云“11.11金秋云创季”活动主会场,阿里云用户通过完成个人或企业实名认证,可以领取不同额度的满减优惠券,叠加折扣优惠。用户购买WoSign SSL证书,如何叠加才能更加优惠呢?
985 3
|
7天前
|
机器学习/深度学习 存储 人工智能
白话文讲解大模型| Attention is all you need
本文档旨在详细阐述当前主流的大模型技术架构如Transformer架构。我们将从技术概述、架构介绍到具体模型实现等多个角度进行讲解。通过本文档,我们期望为读者提供一个全面的理解,帮助大家掌握大模型的工作原理,增强与客户沟通的技术基础。本文档适合对大模型感兴趣的人员阅读。
393 15
白话文讲解大模型| Attention is all you need
|
7天前
|
算法 数据建模 网络安全
阿里云SSL证书2024双11优惠,WoSign DV证书220元/年起
2024阿里云11.11金秋云创季火热进行中,活动月期间(2024年11月01日至11月30日),阿里云SSL证书限时优惠,部分证书产品新老同享75折起;通过优惠折扣、叠加满减优惠券等多种方式,阿里云WoSign SSL证书将实现优惠价格新低,DV SSL证书220元/年起。
559 5
|
3天前
|
安全 网络安全
您有一份网络安全攻略待领取!!!
深入了解如何保护自己的云上资产,领取超酷的安全海报和定制鼠标垫,随时随地提醒你保持警惕!
692 1
您有一份网络安全攻略待领取!!!