Android开发常用Kotlin高级语法

news2025/6/4 9:30:35

一、扩展函数与扩展属性:为系统类 “量身定制” 工具方法

Kotlin 的扩展机制允许为现有类(包括 Java 类)添加新功能,无需继承或修改原类。这在 Android 开发中尤其适合封装高频重复操作(如 View 操作、上下文获取)。

1.1 为 View 添加扩展函数:简化 UI 操作

Android 中操作 View 时,常需要处理可见性、点击事件等重复逻辑。通过扩展函数可以将这些逻辑封装,提升代码简洁性。

// 扩展函数:快速切换View可见性
fun View.setVisible(visible: Boolean) {
    visibility = if (visible) View.VISIBLE else View.GONE
}

// 扩展函数:安全的点击事件(防重复点击)
fun View.setOnSingleClickListener(
    interval: Long = 500L, // 防重复点击间隔
    action: (View) -> Unit
) {
    var lastClickTime = 0L
    setOnClickListener { view ->
        val currentTime = System.currentTimeMillis()
        if (currentTime - lastClickTime > interval) {
            lastClickTime = currentTime
            action(view)
        }
    }
}

// 使用示例
val button = findViewById<Button>(R.id.btn_submit)
button.setVisible(true) // 替代 visibility = View.VISIBLE
button.setOnSingleClickListener { 
    // 点击逻辑(500ms内仅触发一次)
}
1.2 为 Context 添加扩展属性:快速获取系统服务

Android 中获取系统服务(如LayoutInflaterSharedPreferences)需要调用getSystemService(),通过扩展属性可以简化这一过程。

// 扩展属性:获取LayoutInflater
val Context.layoutInflater: LayoutInflater
    get() = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater

// 扩展属性:获取默认SharedPreferences
val Context.defaultSp: SharedPreferences
    get() = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)

// 使用示例(在Activity/Fragment中)
val inflater = layoutInflater // 直接使用扩展属性
val sp = defaultSp.edit().putString("user", "douban").apply()

二、作用域函数:优雅处理对象作用域

Kotlin 提供letrunwithapplyalso等作用域函数,用于在特定作用域内操作对象。选择正确的函数可提升代码可读性。

1.1 作用域函数对比与最佳实践
函数适用场景返回值内部用this还是it
let对对象执行转换或操作,处理null安全最后一行表达式it(单个参数)
run对象配置 + 计算返回值(需同时处理配置和结果)最后一行表达式this(对象自身)
with对单个对象执行多个操作(类似run,但通过参数传递对象)最后一行表达式this(对象自身)
apply对象配置(无需返回值,仅需修改对象属性)对象自身this(对象自身)
also对对象执行副作用操作(如日志、统计,不影响对象本身)对象自身it(单个参数)

Android 开发中的典型用例

// apply:配置View属性(返回View自身,适合链式调用)
val textView = TextView(context).apply {
    text = "Hello Douban"
    textSize = 16f
    setTextColor(ContextCompat.getColor(context, R.color.primary))
    layoutParams = LinearLayout.LayoutParams(
        ViewGroup.LayoutParams.WRAP_CONTENT,
        ViewGroup.LayoutParams.WRAP_CONTENT
    )
}

// let:处理可能为null的值(安全调用链)
intent?.extras?.let { extras -> 
    val userId = extras.getString("user_id")
    userId?.let { loadUser(it) } // 嵌套let处理多级null
}

// also:记录对象状态(不修改对象)
user.also { 
    Log.d("User", "Loaded user: ${it.name}") 
}.run { 
    updateUI(this) // 继续对user执行操作
}

三、object 的核心用法

3.1 单例类(Singleton)

使用 object 关键字可以直接定义一个单例类,无需手动实现单例模式的样板代码。

// 定义单例类
object AppSettings {
    // 成员变量
    var theme: String = "light"
    var isDarkMode: Boolean = false
    
    // 成员函数
    fun saveSettings() {
        // 保存配置到本地
    }
    
    fun loadSettings() {
        // 从本地加载配置
    }
}

// 使用单例(直接通过类名访问)
AppSettings.theme = "dark"
AppSettings.saveSettings()

特点

  • 全局唯一实例,由 Kotlin 编译器自动生成线程安全的单例实现。
  • 可继承类或实现接口,但不能有构造函数(因为无法实例化)。
3.2 对象表达式(匿名内部类)

object 可用于创建匿名类的实例,常用于实现接口或继承类的一次性对象。

// 示例1:实现接口
val listener = object : View.OnClickListener {
    override fun onClick(v: View) {
        // 处理点击事件
    }
}

// 示例2:继承类并扩展
val customRunnable = object : Runnable {
    var counter = 0
    override fun run() {
        counter++
        println("Running $counter times")
    }
}

应用场景

  • 替代 Java 中的匿名内部类(如点击监听器、回调接口)。
  • 临时需要的对象(如单元测试中的 mock 对象)。

四、密封类与数据类:构建安全的状态模型

密封类(Sealed Class)定义受限的类层次结构,适合表示有限的状态类型(如加载状态、网络请求结果);数据类(Data Class)自动生成equals()hashCode()等方法,适合表示数据模型

4.1 密封类:实现状态机(State Machine)

在 MVVM 架构中,常用密封类表示 UI 状态(加载中、成功、失败),确保状态覆盖全面,避免遗漏。

// 密封类:定义网络请求状态
sealed class UserState {
    object Loading : UserState() // 加载中
    data class Success(val user: User) : UserState() // 成功(携带数据)
    data class Error(val exception: Throwable) : UserState() // 失败(携带异常)
}

// ViewModel中更新状态
class UserViewModel : ViewModel() {
    private val _state = MutableLiveData<UserState>()
    val state: LiveData<UserState> = _state

    fun loadUser() {
        _state.value = UserState.Loading // 触发加载状态
        viewModelScope.launch {
            try {
                val user = fetchUser() // 异步获取数据
                _state.value = UserState.Success(user) // 更新为成功状态
            } catch (e: Exception) {
                _state.value = UserState.Error(e) // 更新为失败状态
            }
        }
    }
}

// Activity中根据状态更新UI(配合when表达式,确保覆盖所有情况)
lifecycleScope.launch {
    viewModel.state.collect { state ->
        when (state) {
            is UserState.Loading -> showLoading()
            is UserState.Success -> showUser(state.user)
            is UserState.Error -> showError(state.exception)
        }
    }
}
4.2 数据类:简化数据模型与 Parcelable 集成

数据类默认生成copy()componentN()等方法,适合表示结构化数据。结合@Parcelize注解可快速实现Parcelable接口(用于Intent传参)。

// 数据类(自动生成equals、hashCode、toString)
@Parcelize // 需要添加kotlin-parcelize插件
data class User(
    val id: String,
    val name: String,
    val age: Int,
    val isVip: Boolean = false
) : Parcelable

// 使用示例:通过Intent传递User对象
val user = User(id = "1", name = "Douban", age = 10)
val intent = Intent(context, DetailActivity::class.java).apply {
    putExtra("user", user) // 直接传递Parcelable对象
}
startActivity(intent)

// 在DetailActivity中获取数据
val user = intent.getParcelableExtra<User>("user")

五、内联函数与具体化类型参数:突破泛型擦除限制

内联函数(inline)将函数体直接插入调用处,减少函数调用开销;具体化类型参数(reified)允许在运行时访问泛型类型,解决 Java 泛型擦除问题。

5.1 具体化类型参数:简化泛型类型检查

在 Android 中,常需要根据泛型类型动态创建对象或解析数据(如 JSON 反序列化),具体化类型参数可直接获取类型信息。

// 内联函数:通过反射获取泛型类型
inline fun <reified T> Gson.fromJson(json: String): T {
    return fromJson(json, T::class.java) // T::class.java 可获取具体类型
}

// 使用示例(无需手动传递Type)
val user: User = gson.fromJson(jsonString) 

// 内联函数:启动Activity(泛型指定目标类)
inline fun <reified T : Activity> Context.startActivity() {
    startActivity(Intent(this, T::class.java))
}

// 使用示例
startActivity<MainActivity>() // 替代 startActivity(Intent(this, MainActivity::class.java))

六、协程 (Coroutines):Android 异步编程的 “标准答案”

Android 异步编程中协程的完整实战示例-CSDN博客

七、lazy与lateinit语法对比

7.1 lazy 示例
// 单线程懒加载(Android 主线程)
val expensiveObject: ExpensiveObject by lazy(LazyThreadSafetyMode.NONE) {
    ExpensiveObject() // 首次访问时执行初始化
}

// 多线程安全懒加载(默认模式)
val config: AppConfig by lazy {
    loadConfigFromDisk() // 自动加锁保证线程安全
}
7.2 lateinit 示例
// 在 Activity 中延迟初始化 ViewModel
lateinit var viewModel: MyViewModel

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    viewModel = ViewModelProvider(this).get(MyViewModel::class.java)
    // 必须在使用 viewModel 前初始化
}
7.3 关键差异详解
  • lazy:只能用于 val(只读属性),因为其内部实现通过委托管理初始化值,初始化后不可修改。
  • lateinit:只能用于 var(可变属性),因为需要在后续代码中手动赋值。

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

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

相关文章

输入ifconfig,发现ens33不见了,无法连接至虚拟机

输入ifconfig&#xff0c;发现ens33不见了&#xff0c;无法连接至虚拟机 输入ifconfig&#xff0c;发现ens33不见了&#xff0c;无法连接至虚拟机 输入ifconfig&#xff0c;发现ens33不见了&#xff0c;无法连接至虚拟机 当输入ifconfig&#xff0c;发现少了ens33&#xff0c;无…

Java虚拟机内存区域划分

Java虚拟机内存区域划分 Java虚拟机&#xff08;JVM&#xff09;的内存区域划分主要分为五个部分&#xff1a; 程序计数器&#xff1a;程序计数寄存器&#xff0c;给CPU使用本地方法栈&#xff1a;为JVM使用到的Native方法服务方法区&#xff1a;存储的是编译后的.class文件堆…

网络渗透基础:信息收集

1.信息收集 whois xx.com 域名注册信息 注册人、电话、email Whois.chinaz.com kali自带whois工具 域名备案信息 Beian.miit.gov.cn Tianyancha.com Icp.chinaz.com 爱站 Sou.xiaolanben.com 2.子域名收集 收集方式 枚举&#xff1a;基于字典搜索引擎&#xff1a;googleh…

[SAP] 如何查询当前屏幕的Tcode?

事务代码Tcode是SAP中到达特定屏幕的快捷路径 如何查询以下屏幕的事务码Tcode&#xff1f; 要浏览当前所使用的屏幕的事务码&#xff0c;可以选择System | Status 这里的事务代码是[VA22]&#xff0c;它是Change Quotation的事务代码

ZigBee 协议:开启物联网低功耗通信新时代

在物联网蓬勃发展的时代&#xff0c;无线通信技术犹如连接万物的桥梁&#xff0c;而 ZigBee 协议以其独特的优势&#xff0c;在众多通信协议中脱颖而出&#xff0c;成为构建低功耗、可靠物联网网络的关键技术之一。 一、ZigBee 协议的起源与发展 ZigBee 这个名字充满了自然的灵…

JavaScript 模块系统:CJS/AMD/UMD/ESM

文章目录 前言一、CommonJS (CJS) - Node.js 的同步模块系统1.1 设计背景1.2 浏览器兼容性问题1.3 Webpack 如何转换 CJS1.4 适用场景 二、AMD (Asynchronous Module Definition) - 浏览器异步加载方案2.1 设计背景2.2 为什么现代浏览器不原生支持 AMD2.3 Webpack/Rollup 如何处…

STM32F407寄存器操作(ADC非连续扫描模式)

1.前言 书接上回&#xff0c;在看手册的时候我突然发现手册上还描述了另一种ADC扫描模式&#xff0c;即非连续扫描模式&#xff0c;想着连续扫描模式都已经探索过了&#xff0c;那就顺手把非非连续模式研究一下吧。 2.理论 我们先看看手册&#xff0c;这里我就以规则通道举例…

PHP学习笔记(十一)

类常量 可以把在类中始终保持不变的值定义为常量&#xff0c;类常量的默认可见性是public。 接口中也可以定义常量。 可以用一个变量来动态调用类&#xff0c;但该变量的值不能为关键字 需要注意的是类常量只为每个类分配一次&#xff0c;而不是为每个类的实例分配。 特殊的…

直线模组在手术机器人中有哪些技术挑战?

手术机器人在现代医疗领域发挥着越来越重要的作用&#xff0c;直线模组作为其关键部件&#xff0c;对手术机器人的性能有着至关重要的影响。然而&#xff0c;在手术机器人中使用直线模组面临着诸多技术挑战&#xff0c;具体如下&#xff1a; 1、‌高精度要求‌&#xff1a;手术…

RK3568DAYU开发板-平台驱动开发--UART

1、程序介绍 本程序是基于OpenHarmony标准系统编写的平台驱动案例&#xff1a;UART 系统版本:openharmony5.0.0 开发板:dayu200 编译环境:ubuntu22 部署路径&#xff1a; //sample/06_platform_uart 2、基础知识 2.1、UART简介 UART指异步收发传输器&#xff08;Univer…

制造企业搭建AI智能生产线怎么部署?

制造商需要精准协调生产和发货&#xff0c;确保订单及时交付。MES、ERP、CRM 系统与生产线集成&#xff0c;对生产管理流程、物料跟踪、品控、确定货期至关重要。如果某个系统发生延迟或者效率低下&#xff0c;会在造成整个生产环节停滞&#xff0c;影响最终交付&#xff0c;导…

深度学习驱动的超高清图修复技术——综述

Deep Learning-Driven Ultra-High-Definition Image Restoration: A Survey Liyan Wang, Weixiang Zhou, Cong Wang, Kin-Man Lam, Zhixun Su, Jinshan Pan Abstract Ultra-high-definition (UHD) image restoration​​ aims to specifically solve the problem of ​​quali…

Azure DevOps 管道部署系列之二IIS

本博客旨在提供如何使用 Azure DevOps YAML 管道部署到虚拟机上的 IIS 的实用指南。 开始之前,您需要做好以下准备: 您拥有要部署的服务器的访问权限以及 PowerShell 的管理员访问权限。您拥有要部署的远程服务器的互联网访问权限。您拥有在服务器上安装 .NET Core 托管包的…

Vue.js教学第十七章:Vue 与后端交互(一),Axios 基础

Vue 与后端交互(一):Axios 基础 在现代前端开发中,Vue 应用通常需要与后端 API 进行数据交互,以实现动态数据的获取和提交。Axios 是一个基于 Promise 的 HTTP 客户端,广泛用于 Vue 项目中与后端进行通信。本文将深入讲解 Axios 的基本用法,包括如何通过 Axios 发送 GE…

人工智能浪潮下,制造企业如何借力DeepSeek实现数字化转型?

一、DeepSeek技术概述 DeepSeek&#xff0c;凭借其强大的深度学习和自然语言处理能力&#xff0c;能够理解复杂问题并提供精准解决方案。它不仅能够作为学习、工作、生活的助手&#xff0c;满足用户在不同场景下的需求&#xff0c;更能在制造业中发挥重要作用。通过自然语言交…

从线性代数到线性回归——机器学习视角

真正不懂数学就能理解机器学习其实是个神话。我认为&#xff0c;AI 在商业世界可以不懂数学甚至不懂编程也能应用&#xff0c;但对于技术人员来说&#xff0c;一些基础数学是必须的。本文收集了我认为理解学习本质所必需的数学基础&#xff0c;至少在概念层面要掌握。毕竟&…

计算机网络相关发展以及常见性能指标

目录 一、因特网概述 1.1 基本概念 1.2 因特网发展的三个阶段 1.3 英特网服务提供者ISP 1.4 英特网的标准化工作 1.5 因特网的组成 1.6 简单总结 二、3种交换方式 2.1 电路交换&#xff08;Circuit Switching&#xff09; 2.2 分组交换&#xff08;Packet Switching&…

通义灵码:基于MCP的火车票小助手系统全流程设计与技术总结

具体操作步骤请访问&#xff1a;https://blog.csdn.net/ailuloo/article/details/148319336?spm1001.2014.3001.5502 前沿技术应用全景图 一、项目背景与需求分析 目标&#xff1a;基于12306 MCP接口&#xff0c;开发一款解决高峰出行&#xff08;春运/节假日&#xff09;痛…

为什么建立 TCP 连接时,初始序列号不固定?

主要原因有两个方面&#xff1a; 很大程度上避免历史报文被下一个相同四元组的 TCP 连接接收问题&#xff08;主要方面&#xff09;防止黑客伪造相同序列号的 TCP 报文被接收 接下来&#xff0c;详细说说第一点 假设每次建立 TCP 连接时&#xff0c;客户端和服务端的初始序列…

VBA数据库解决方案二十:Select表达式From区域Where条件Order by

《VBA数据库解决方案》教程&#xff08;版权10090845&#xff09;是我推出的第二套教程&#xff0c;目前已经是第二版修订了。这套教程定位于中级&#xff0c;是学完字典后的另一个专题讲解。数据库是数据处理的利器&#xff0c;教程中详细介绍了利用ADO连接ACCDB和EXCEL的方法…