Kotlin基础(七):数据类和封闭类

news2024/11/27 15:34:07

前言

本文主要讲解kotlin数据类(DataClass)和封闭类(SealedClasses),包括使用数据类,对象复制,数据类成员的解构,使用封闭类,以及数据类和封闭类在Android开发中的应用。


Kotlin文章列表

Kotlin文章列表: 点击此处跳转查看


目录

在这里插入图片描述


1.1 数据类(DataClass)

Kotlin提供了许多简洁、安全和表达性强的语法特性。其中一个非常有用的特性是数据类(data class)。

数据类是 Kotlin 中用于表示纯数据的一种特殊类。它们通常用于封装数据,并且提供了一些有用的功能,如自动生成的 equals()hashCode()toString() 方法,以及 copy() 方法。这些方法允许你轻松地比较、复制和打印数据类的实例。

要声明一个数据类,你可以使用 data 关键字,后面紧跟着类的声明。以下是一个简单的数据类的例子:

data class Person(val name: String, val age: Int)

在上面的例子中,Person 是一个数据类,它有两个属性 nameage。Kotlin 会自动为数据类生成以下方法:

  • equals(other: Any?):用于比较两个对象是否相等。
  • hashCode():用于生成对象的哈希码。
  • toString():用于将对象转换为字符串的表示形式。
  • copy():用于复制对象。

你可以使用这些方法来进行对象的比较、复制和打印操作。例如:

val person1 = Person("Alice", 25)
val person2 = Person("Alice", 25)

println(person1 == person2)  // 输出: true

val person3 = person1.copy(age = 30)
println(person3)  // 输出: Person(name=Alice, age=30)

除了自动生成的方法外,数据类还可以继承自其他类、实现接口,以及定义额外的属性和方法。

总结来说,Kotlin 的数据类提供了一种方便的方式来表示纯数据,并自动为你生成一些常用的方法,减少了编写样板代码的工作量。

1.1.1 使用数据类

在 Kotlin 中,使用数据类(data class)非常简单。你可以按照以下步骤使用数据类:

  1. 声明数据类:使用 data 关键字声明数据类,并定义它的属性。
data class Person(val name: String, val age: Int)
  1. 创建数据类的实例:使用构造函数来创建数据类的实例,并传入相应的属性值。
val person = Person("Alice", 25)
  1. 访问数据类的属性:使用点表示法来访问数据类的属性。
println(person.name)  // 输出: Alice
println(person.age)   // 输出: 25
  1. 比较数据类的实例:数据类自动生成了 equals() 方法,可以直接用于比较两个对象是否相等。
val person1 = Person("Alice", 25)
val person2 = Person("Alice", 25)

println(person1 == person2)  // 输出: true
  1. 复制数据类的实例:数据类自动生成了 copy() 方法,可以用于创建数据类实例的副本,并可以选择性地修改属性的值。
val person3 = person1.copy(age = 30)
println(person3)  // 输出: Person(name=Alice, age=30)
  1. 其他自动生成的方法:数据类还自动生成了 hashCode()toString() 方法。hashCode() 方法用于生成对象的哈希码,toString() 方法用于将对象转换为字符串的表示形式。
println(person.hashCode())  // 输出: 根据属性值生成的哈希码
println(person.toString())  // 输出: Person(name=Alice, age=25)

需要注意的是,数据类的主构造函数必须至少有一个参数。如果你希望某个属性不参与比较、复制或生成哈希码,可以将其声明为 valvar 之前加上 @Transient 注解。

data class Person(val name: String, @Transient val age: Int)

这样,age 属性将被忽略,并不会影响生成的方法。

总之,Kotlin 的数据类提供了一种方便的方式来定义和使用纯数据,并自动为你生成一些常用的方法,简化了对数据的操作。

1.1.2 对象复制

在 Kotlin 中,你可以使用数据类(data class)自动生成的 copy() 方法来复制对象并创建其副本。这个方法允许你选择性地修改副本的属性值。

以下是使用 copy() 方法进行对象复制的示例:

data class Person(val name: String, val age: Int)

val person1 = Person("Alice", 25)
val person2 = person1.copy()

println(person1)  // 输出: Person(name=Alice, age=25)
println(person2)  // 输出: Person(name=Alice, age=25)
println(person1 === person2)  // 输出: false(两个对象不是同一个引用)

在上面的例子中,person1 是一个 Person 对象,通过调用 copy() 方法创建了 person2 的副本。copy() 方法会复制所有属性的值,并返回一个新的对象。

你还可以选择性地修改副本的属性值。可以通过在 copy() 方法中指定新的属性值来实现:

val person3 = person1.copy(age = 30)
println(person3)  // 输出: Person(name=Alice, age=30)

在上面的例子中,person3person1 的副本,但是修改了 age 属性的值。

需要注意的是,copy() 方法只复制数据类的属性值,而不会复制其他相关对象的引用。如果你的数据类包含其他对象类型的属性,它们将会被浅复制,也就是复制引用而不是实际对象。如果需要深复制,需要手动处理相关对象的复制逻辑。

总结来说,使用数据类自动生成的 copy() 方法可以轻松地复制对象并创建副本。你可以选择性地修改副本的属性值,而不影响原始对象。这是 Kotlin 中方便且灵活的对象复制机制。

1.1.3 数据类成员的解构

在 Kotlin 中,数据类(data class)的成员可以通过解构(destructuring)来访问和使用。解构允许你将一个对象的属性拆解为多个独立的变量,以便更方便地使用这些属性。

要使用解构,你可以将一个数据类的实例赋值给多个变量。每个变量的名称与数据类的属性名称相对应。以下是一个使用解构的示例:

data class Person(val name: String, val age: Int)

val person = Person("Alice", 25)
val (name, age) = person

println(name)  // 输出: Alice
println(age)   // 输出: 25

在上面的例子中,我们将 person 对象解构为两个变量 nameage。这样,我们可以直接使用这两个变量来访问对象的属性值。

解构不仅限于单个数据类对象,还可以在集合中使用。例如,你可以解构一个包含多个数据类对象的列表:

val people = listOf(
    Person("Alice", 25),
    Person("Bob", 30),
    Person("Charlie", 35)
)

for ((name, age) in people) {
    println("Name: $name, Age: $age")
}

在上面的例子中,我们使用解构在循环中访问 people 列表中每个对象的属性。通过解构,我们可以直接在循环体内使用 nameage 变量。

需要注意的是,解构只能用于访问数据类的属性。如果你需要访问其他方法或自定义的成员,仍然需要通过对象名称来调用。

总结来说,Kotlin 中的解构允许你将数据类对象的属性拆解为多个独立的变量,以便更方便地使用这些属性。你可以在赋值语句或循环中使用解构来访问数据类的成员。这是一种简洁、便利的语言特性,可以提高代码的可读性和易用性。

1.1.4 kotlin数据类在Android开发中的应用

在Android开发中,Kotlin数据类非常实用,特别是用于存储和传递数据对象,比如表示网络请求的数据模型、Intent传递的数据对象、RecyclerView的数据项等。下面将分别举例说明Kotlin数据类在这些场景中的应用。

示例1:网络请求数据模型

假设我们有一个简单的用户数据模型,用于表示从服务器获取的用户信息,包含用户名和年龄。

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

示例2:Intent传递数据

当我们需要在Activity之间传递数据时,Kotlin数据类可以帮助我们轻松地创建可序列化的对象,以便Intent传递数据。

在发送端Activity:

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

data class Person(val name: String, val age: Int)

class SenderActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sender)

        val person = Person("John Doe", 30)

        val intent = Intent(this, ReceiverActivity::class.java)
        intent.putExtra("PERSON_DATA", person)
        startActivity(intent)
    }
}

在接收端Activity:

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

data class Person(val name: String, val age: Int)

class ReceiverActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_receiver)

        val person = intent.getSerializableExtra("PERSON_DATA") as? Person
        if (person != null) {
            // 使用接收到的Person对象进行操作
            // 例如:显示名称和年龄
        }
    }
}

示例3:RecyclerView的数据项

在RecyclerView中,我们经常需要使用数据类来表示每个列表项的数据。

首先,在RecyclerView的适配器中定义一个数据类,用于表示列表项的数据:

data class Item(val title: String, val description: String)

然后,在适配器中使用这个数据类:

class ItemAdapter(private val itemList: List<Item>) : RecyclerView.Adapter<ItemAdapter.ItemViewHolder>() {

    // ... 适配器的其他代码 ...

    override fun onBindViewHolder(holder: ItemViewHolder, position: Int) {
        val currentItem = itemList[position]
        holder.bind(currentItem)
    }

    inner class ItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        fun bind(item: Item) {
            // 在ViewHolder中绑定数据到视图
            itemView.titleTextView.text = item.title
            itemView.descriptionTextView.text = item.description
        }
    }
}

这样,我们就可以在RecyclerView中使用数据类Item来表示每个列表项的数据,并通过适配器将数据绑定到对应的视图上。

总结:
Kotlin数据类在Android开发中广泛用于数据对象的定义和传递。它们简洁、类型安全且提供了自动生成的方法,极大地简化了Android开发中数据处理的代码。在网络请求的数据模型、Intent传递数据以及RecyclerView的数据项等场景中,Kotlin数据类都能发挥重要的作用。

1.2 封闭类(SealedClasses)

1.2.1 使用封闭类

在 Kotlin 中,封闭类(sealed class)是一种特殊的类,用于限制类的继承层级。封闭类的所有子类必须在同一文件中定义,并且不能在类外部继续扩展。

封闭类用于表示一组相关的类或状态,并且通常与模式匹配(pattern matching)一起使用。它可以作为一个类的抽象基类,定义一些通用的行为和属性,然后由其子类实现具体的细节。

以下是一个封闭类的示例:

sealed class Result
data class Success(val data: String) : Result()
data class Error(val message: String) : Result()
object Loading : Result()

在上面的示例中,Result 是一个封闭类。它有三个子类:SuccessErrorLoadingSuccessError 是数据类,它们分别携带了一个属性 datamessageLoading 是一个单例对象。

封闭类的优点在于,当你使用 when 表达式(或者 if 表达式)进行模式匹配时,编译器会强制要求你处理所有可能的子类情况。这样可以确保代码的完整性,避免遗漏某些情况。

以下是一个使用封闭类的模式匹配的示例:

fun processResult(result: Result) {
    when (result) {
        is Success -> println("Success: ${result.data}")
        is Error -> println("Error: ${result.message}")
        Loading -> println("Loading...")
    }
}

val result1: Result = Success("Data loaded successfully")
val result2: Result = Error("Failed to load data")
val result3: Result = Loading

processResult(result1)  // 输出: Success: Data loaded successfully
processResult(result2)  // 输出: Error: Failed to load data
processResult(result3)  // 输出: Loading...

在上面的示例中,processResult() 函数接受一个 Result 对象,并使用 when 表达式根据不同的子类情况进行处理。

需要注意的是,封闭类的子类可以定义在封闭类内部,也可以定义在同一文件的顶层。这取决于你的需求和代码的组织结构。

总结来说,封闭类是 Kotlin 中用于限制类的继承层级的一种特殊类。它通常与模式匹配一起使用,可以确保处理所有可能的子类情况,提高代码的安全性和可读性。

1.2.2 kotlin封闭类在Android开发中的应用

在Android开发中,Kotlin的封闭类(Sealed Class)可以用于表示特定状态、状态机或表达式的状态,并且限制这些状态的范围。封闭类非常适用于处理复杂的状态切换或表示一组有限的相关状态。

下面将分别举例说明Kotlin封闭类在Android开发中的两个应用场景:表示加载状态和表达式求值。

示例1:表示加载状态

假设我们有一个简单的数据请求的场景,我们需要表示数据的三种状态:加载中、加载成功和加载失败。

首先,定义一个封闭类DataState来表示这些状态:

sealed class DataState {
    object Loading : DataState()
    data class Success(val data: String) : DataState()
    data class Error(val message: String) : DataState()
}

接下来,在某个ViewModel中使用这个封闭类来表示数据的不同状态:

class DataViewModel : ViewModel() {
    private val _dataState = MutableLiveData<DataState>()
    val dataState: LiveData<DataState>
        get() = _dataState

    fun fetchData() {
        // 模拟数据请求,这里用延时来模拟网络请求的过程
        _dataState.value = DataState.Loading

        // 假设在此处进行实际的数据请求,并根据请求结果更新_dataState的值
        // 如果请求成功,则设置DataState.Success(data);如果请求失败,则设置DataState.Error(message)
    }
}

接着,在Activity或Fragment中观察DataViewModel中的dataState,根据不同的状态进行相应的UI展示:

class DataActivity : AppCompatActivity() {

    private val dataViewModel: DataViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_data)

        dataViewModel.dataState.observe(this, { dataState ->
            when (dataState) {
                is DataState.Loading -> showLoadingView()
                is DataState.Success -> showData(dataState.data)
                is DataState.Error -> showError(dataState.message)
            }
        })

        fetchData()
    }

    private fun fetchData() {
        dataViewModel.fetchData()
    }

    private fun showLoadingView() {
        // 显示加载中的UI
    }

    private fun showData(data: String) {
        // 显示数据的UI,使用传递过来的data
    }

    private fun showError(message: String) {
        // 显示错误的UI,使用传递过来的错误消息
    }
}

示例2:表达式求值

另一个应用场景是用封闭类来表示表达式的求值结果,例如简单的加法和减法表达式:

sealed class Expression {
    data class Add(val num1: Int, val num2: Int) : Expression()
    data class Subtract(val num1: Int, val num2: Int) : Expression()
}

然后,我们可以定义一个函数来计算这些表达式:

fun evaluateExpression(expression: Expression): Int {
    return when (expression) {
        is Expression.Add -> expression.num1 + expression.num2
        is Expression.Subtract -> expression.num1 - expression.num2
    }
}

在使用时,可以创建不同的表达式实例,并传递给evaluateExpression函数来得到求值结果:

val addExpression = Expression.Add(5, 3)
val subtractExpression = Expression.Subtract(10, 2)

val addResult = evaluateExpression(addExpression) // 结果为 8
val subtractResult = evaluateExpression(subtractExpression) // 结果为 8

总结:
Kotlin封闭类在Android开发中的应用场景有很多,其中上述两个示例是其中的两个常见场景:表示加载状态和表达式求值。通过使用封闭类,我们可以将相关状态或表达式集中在一个类中,并限制这些状态或表达式的范围,从而使代码更加清晰、易于维护。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据挖掘】时间序列的傅里叶变换:用numpy解释的快速卷积

一、说明 本篇告诉大家一个高级数学模型&#xff0c;即傅里叶模型的使用&#xff1b; 当今&#xff0c;傅里叶变换及其所有变体构成了我们现代世界的基础&#xff0c;为压缩、通信、图像处理等技术提供了动力。我们从根源上理解&#xff0c;从根本上应用&#xff0c;这是值得付…

微信小程序——页面跳转方法和场景用法总结

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

失物招领小程序连接人与物的奇妙纽带

hello guys!! 随着生活的节奏加快&#xff0c;人们在各个领域都有可能会遇到丢失物品或者拾到物品的情况。不论是学生、员工还是旅游爱好者&#xff0c;我们都有可能在生活的轨迹中遇到这样的情况。为了提供一个便捷的平台&#xff0c;让人们能够分享、发布和寻找丢失物品&…

再添新品|OPT(奥普特)高速高分辨率线阵相机发布!

针对大幅面且高速生产的视觉检测场景&#xff0c;OPT&#xff08;奥普特&#xff09;持续在数据传输接口技术上进行开发创新&#xff0c;推出三大系列线阵相机&#xff0c;产品阵容再升级。 本次发布的新品共12款&#xff0c;分别有万兆网、CXP及CL系列的新品&#xff0c;分辨…

JavaDemo——使用jks的https

java使用https主要就是设置下sslContext&#xff0c;sslContext初始化需要密钥管理器和信任管理器&#xff0c;密钥管理器用于管理本地证书和私钥&#xff0c;信任管理器用于验证远程服务器的证书&#xff0c;这两种管理器都需要KeyStore初始化&#xff0c;两种管理器可以按需只…

OS1_进程与线程的管理

序言 1.OS以进程、线程的方式在CPU中执行静态保存在外存(内存)中的程序&#xff0c;进程的构成与状态转化&#xff0c;特别是进程的切换&#xff1b; 2.当有多个进程处于就绪态&#xff0c;有哪些常见的挑选以执行方式&#xff1b; 3.并发执行(乱序发射)的进程&#xff0c;共享…

商品信息管理-亿发商品进销存管理系统,批发行业零售门店免费试用

众所周知&#xff0c;批发零售行业面临着商品品类繁多、品牌众多、商品信息量庞大等挑战。同时&#xff0c;商品售价波动频繁&#xff0c;还需要管理商品批次&#xff0c;避免积压过期。针对这些传统批发零售行业的管理难题&#xff0c;加快行业数字化转型成为解决之道&#xf…

远程访问不了虚拟机【bug】

远程访问不了虚拟机【bug】 bug 虚拟机访问不了他的默认网关 虚拟机IP&#xff1a;172.25.254.250 虚拟机网关IP&#xff1a;172.25.254.1 远程登录也是超时的 错误产生 我还原了一下虚拟机的网络配置选项 导致 √ 使用本地DHCPT服务将IP地址给虚拟机 相关资源 本机的I…

基于Labelstudio的UIE半监督智能标注方案(本地版)

自然语言处理信息抽取智能标注方案包括以下几种&#xff1a; 基于规则的标注方案&#xff1a;通过编写一系列规则来识别文本中的实体、关系等信息&#xff0c;并将其标注。 基于规则的标注方案是一种传统的方法&#xff0c;它需要人工编写规则来识别文本中的实体、关系等信息&a…

Redis入门(1)——Redis是啥 安装Docker的Redis Redis的基本数据类型+常用命令 SpringBoot整合Redis初步

目录 引出MySQL数据库&#xff1a;慢Redis是啥&#xff1f;问题&#xff1a;redis是单线程的&#xff0c;为什么会非常快&#xff1f; 安装Redis的docker1.搜索docker search redis2.拉取docker pull redis3.运行容器4.进入容器-->进入redis redis的基本数据类型字符串(stri…

在线招投标系统nodejs+vue

本站是一个B/S模式系统&#xff0c;采用vue框架&#xff0c;MYSQL数据库设计开发&#xff0c;充分保证系统的稳定性。系统具有界面清晰、操作简单&#xff0c;功能齐全的特点&#xff0c;使得在线招投标系统管理工作系统化、规范化。本系统的使用使管理人员从繁重的工作中解脱出…

PHP反序列化漏洞之面向对象基础

一、PHP面向对象基础 要谈PHP反序列化&#xff0c;不得不涉及面向对象&#xff0c;因为在反序列化漏洞利用中&#xff0c;大多涉及的都是“对象”的反序列化。所以需要了解面向对象基础。 面向面向对象是一种以“对象”为中心的编程思想&#xff0c;把要解决的问题分解成各个…

WORD模板如何自定义并使用?

文章目录 0.引言1.新建WORD2.WORD另存为模板3.使用模板 0.引言 使用Word模板可以提高文档处理的一致性、效率和专业性&#xff0c;同时也方便了更新和维护。对于需要频繁创建或修改文档的组织或个人来说&#xff0c;使用Word模板是一个非常实用的工具。本文总结Word模板自定义并…

Docker数据管理和镜像创建

Docker数据管理和镜像创建 一、Docker的数据管理1、数据卷2、数据卷容器3、端口映射4、容器互联&#xff08;使用centos镜像&#xff09; 二、Docker 镜像的创建1、基于现有镜像创建2、基于本地模板创建3、基于Dockerfile创建3.1 联合文件系统&#xff08;UnionFS&#xff09;3…

Docker 的前世今生

&#x1f337;&#x1f341; 博主 libin9iOak带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——libin9iOak的博客&#x1f390; &#x1f433; 《面试题大全》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33…

19 数组静态初始化练习

语法&#xff1a; 数据类型[ ] 数组名称 {元素1&#xff0c;元素2&#xff0c;元素3}; public class Demo1 {public static void main(String[] args) {int[] arr {0,1,2,3};System.out.println(arr);System.out.println(arr[0]);System.out.println(arr[1]);System.out.pri…

线性链表的实现

线性链表简介 线性表的链式存储结构称为线性链表&#xff0c;如图1所示&#xff0c;线性链表将存储空间划分成若干的小块&#xff0c;每块占用若干个字节&#xff0c;这些小块称为存储结点。将其中的存储结点分为两个部分&#xff0c;一部分用于存储数据元素的值&#xff0c;称…

vue中如何通过webpack-bundle-analyzer打包分析工具进行配置优化

vue中随着项目的不断功能迭代和开发&#xff0c;项目文件越来越多&#xff0c;项目的打包文件也越来越大。如何对打包文件进行分析优化&#xff0c;减小打包文件大小呢&#xff1f;可以通过webpack-bundle-analyzer 这个打包分析工具进行解决。 1、webpack-bundle-analyzer的安…

传输层协议 TCP与UDP

目录 传输层端口号端口号范围划分 0-65535认识知名端口号(Well-Know Port Number)netstatpidofxargs UDP协议UDP协议段格式UDP的特点面向数据报UDP的缓冲区基于UDP的应用层协议 TCP协议TCP协议段格式确认应答(ACK)机制超时重传机制连接管理机制&#xff1a;tcp的三次握手和四次…

自然语言处理基础详解入门

1、自然语言的概念 自然语言是指人类社会约定俗成的&#xff0c;并且区别于人工语言&#xff08;如计算机程序&#xff09;的语言&#xff0c;&#xff0c;是自然而然的随着人类社会发展演变而来的语言&#xff0c;它是人类学习生活的重要工具。 2、自然语言处理概述 自然语言…