1.4 实战案例:用 Kotlin 重写一个 Java Android 工具类
1.4.1 需求背景
在 Android 开发中,我们经常需要一些工具类来处理常见任务,如:
- 字符串处理
- 日期时间转换
- 密度转换(dp、px、sp)
- 输入验证
这些工具类在 Java 中通常写成静态方法,但在 Kotlin 中可以更加优雅。
1.4.2 Java 版本的工具类
java
// Java 版本的工具类
package com.example.tools;
import java.util.regex.Pattern;
public class StringUtils {
// 判断字符串是否为空
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
// 判断字符串是否不为空
public static boolean isNotEmpty(String str) {
return !isEmpty(str);
}
// 判断字符串是否为空或空白
public static boolean isBlank(String str) {
return str == null || str.trim().length() == 0;
}
// 邮箱验证
public static boolean isEmail(String email) {
if (isEmpty(email)) {
return false;
}
String emailRegex = "^[A-Za-z0-9+_.-]+@(.+)$";
Pattern pattern = Pattern.compile(emailRegex);
return pattern.matcher(email).matches();
}
// 手机号验证(简单版)
public static boolean isPhoneNumber(String phone) {
if (isEmpty(phone)) {
return false;
}
String phoneRegex = "^1[3-9]\\d{9}$";
Pattern pattern = Pattern.compile(phoneRegex);
return pattern.matcher(phone).matches();
}
// 首字母大写
public static String capitalize(String str) {
if (isEmpty(str)) {
return str;
}
return str.substring(0, 1).toUpperCase() + str.substring(1);
}
}
1.4.3 Kotlin 版本的重写
方式一:扩展函数版本(推荐)
kotlin
// Kotlin 扩展函数版本
import java.util.regex.Pattern
// 扩展 String 的 isEmpty 函数
fun String?.isEmptyOrNull(): Boolean {
return this == null || this.isEmpty()
}
// 扩展 String 的 isBlank 函数
fun String?.isBlankOrNull(): Boolean {
return this == null || this.isBlank()
}
// 扩展 String 的邮箱验证
fun String?.isEmail(): Boolean {
if (this.isNullOrBlank()) return false
val emailRegex = "^[A-Za-z0-9+_.-]+@(.+)$"
val pattern = Pattern.compile(emailRegex)
return pattern.matcher(this).matches()
}
// 扩展 String 的手机号验证
fun String?.isPhoneNumber(): Boolean {
if (this.isNullOrBlank()) return false
val phoneRegex = "^1[3-9]\\d{9}$"
val pattern = Pattern.compile(phoneRegex)
return pattern.matcher(this).matches()
}
// 扩展 String 的首字母大写
fun String.capitalize(): String {
if (this.isEmpty()) return this
return this.substring(0, 1).toUpperCase() + this.substring(1)
}
使用对比:
java
// Java 使用方式
String email = "test@example.com";
if (StringUtils.isEmpty(email)) {
// 处理空字符串
}
if (StringUtils.isEmail(email)) {
// 处理邮箱验证通过
}
kotlin
// Kotlin 使用方式
val email: String? = "test@example.com"
if (email.isNullOrBlank()) {
// 处理空字符串
}
if (email.isEmail()) {
// 处理邮箱验证通过
}
方式二:工具类版本(兼容现有代码)
kotlin
// Kotlin 工具类版本(使用伴生对象) 直接通过AS通过Java转Kotlin代码
package com.example.tools
import java.util.Locale
import java.util.regex.Pattern
object StringUtils {
// 判断字符串是否为空
fun isEmpty(str: String?): Boolean {
return str == null || str.length == 0
}
// 判断字符串是否不为空
fun isNotEmpty(str: String?): Boolean {
return !isEmpty(str)
}
// 判断字符串是否为空或空白
fun isBlank(str: String?): Boolean {
return str == null || str.trim { it <= ' ' }.length == 0
}
// 邮箱验证
fun isEmail(email: String?): Boolean {
if (isEmpty(email)) {
return false
}
val emailRegex = "^[A-Za-z0-9+_.-]+@(.+)$"
val pattern = Pattern.compile(emailRegex)
return pattern.matcher(email).matches()
}
// 手机号验证(简单版)
fun isPhoneNumber(phone: String?): Boolean {
if (isEmpty(phone)) {
return false
}
val phoneRegex = "^1[3-9]\\d{9}$"
val pattern = Pattern.compile(phoneRegex)
return pattern.matcher(phone).matches()
}
// 首字母大写 可以增加注解@JvmStatic
@JvmStatic
fun capitalize(str: String?): String? {
if (isEmpty(str)) {
return str
}
return str!!.substring(0, 1).uppercase(Locale.getDefault()) + str.substring(1)
}
}
@JvmStatic 注解:让 Java 代码可以像调用静态方法一样调用 Kotlin 的伴生对象方法。
1.4.4 Android 密度转换工具类
需求:在 Android 开发中,经常需要在 dp、px、sp 之间进行转换。
kotlin
import android.content.Context
import android.util.TypedValue
// 扩展 Context 的密度转换函数
fun Context.dpToPx(dp: Float): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp,
this.resources.displayMetrics
)
}
fun Context.pxToDp(px: Float): Float {
return px / this.resources.displayMetrics.density
}
fun Context.spToPx(sp: Float): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
sp,
this.resources.displayMetrics
)
}
fun Context.pxToSp(px: Float): Float {
return px / this.resources.displayMetrics.scaledDensity
}
// 扩展 Int 的密度转换函数
fun Int.dpToPx(context: Context): Int {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this.toFloat(),
context.resources.displayMetrics
).toInt()
}
fun Int.spToPx(context: Context): Int {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
this.toFloat(),
context.resources.displayMetrics
).toInt()
}
使用示例:
kotlin
// 在 Activity 或 Fragment 中使用
val marginInDp = 16
val marginInPx = marginInDp.dpToPx(this)
val textSizeInSp = 14
val textSizeInPx = textSizeInSp.spToPx(this)
// 或者直接使用 Context 扩展函数
val pxValue = this.dpToPx(16f)
val dpValue = this.pxToDp(48f)
1.4.5 完整的工具类封装
kotlin
package com.example.androidmoderndev.utils
import android.content.Context
import android.util.TypedValue
import java.text.SimpleDateFormat
import java.util.*
/**
* 字符串扩展函数
*/
// 判断字符串是否为空或 null
fun String?.isEmptyOrNull(): Boolean = this.isNullOrEmpty()
// 判断字符串是否为空白或 null
fun String?.isBlankOrNull(): Boolean = this.isNullOrBlank()
// 邮箱验证
fun String?.isEmail(): Boolean {
if (this.isNullOrBlank()) return false
val emailRegex = "^[A-Za-z0-9+_.-]+@(.+)$"
val pattern = Regex(emailRegex)
return pattern.matches(this)
}
// 手机号验证(中国大陆)
fun String?.isPhoneNumber(): Boolean {
if (this.isNullOrBlank()) return false
val phoneRegex = "^1[3-9]\\d{9}$"
val pattern = Regex(phoneRegex)
return pattern.matches(this)
}
// 首字母大写
fun String.capitalize(): String {
if (this.isEmpty()) return this
return this.substring(0, 1).toUpperCase() + this.substring(1)
}
// 验证密码强度(至少8位,包含字母和数字)
fun String?.isStrongPassword(): Boolean {
if (this.isNullOrBlank() || this.length < 8) return false
val hasLetter = this.any { it.isLetter() }
val hasDigit = this.any { it.isDigit() }
return hasLetter && hasDigit
}
/**
* 日期时间扩展函数
*/
// Date 转格式化字符串
fun Date.toFormattedString(pattern: String = "yyyy-MM-dd HH:mm:ss"): String {
val sdf = SimpleDateFormat(pattern, Locale.getDefault())
return sdf.format(this)
}
// 时间戳转格式化字符串
fun Long.toFormattedString(pattern: String = "yyyy-MM-dd HH:mm:ss"): String {
val sdf = SimpleDateFormat(pattern, Locale.getDefault())
return sdf.format(Date(this))
}
/**
* Context 扩展函数(密度转换)
*/
fun Context.dpToPx(dp: Float): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
dp,
this.resources.displayMetrics
)
}
fun Context.pxToDp(px: Float): Float {
return px / this.resources.displayMetrics.density
}
fun Context.spToPx(sp: Float): Float {
return TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
sp,
this.resources.displayMetrics
)
}
fun Context.pxToSp(px: Float): Float {
return px / this.resources.displayMetrics.scaledDensity
}
/**
* Int 扩展函数(密度转换)
*/
fun Int.dpToPx(context: Context): Int {
return context.dpToPx(this.toFloat()).toInt()
}
fun Int.spToPx(context: Context): Int {
return context.spToPx(this.toFloat()).toInt()
}
/**
* SharedPreferences 扩展函数
*/
import android.content.SharedPreferences
fun SharedPreferences.putString(key: String, value: String?) {
edit().putString(key, value).apply()
}
fun SharedPreferences.getInt(key: String, defaultValue: Int = 0): Int {
return getInt(key, defaultValue)
}
fun SharedPreferences.getBoolean(key: String, defaultValue: Boolean = false): Boolean {
return getBoolean(key, defaultValue)
}
fun SharedPreferences.putBoolean(key: String, value: Boolean) {
edit().putBoolean(key, value).apply()
}
1.4.6 代码对比总结
| 方面 | Java 版本 | Kotlin 版本 |
|---|---|---|
| 代码量 | 约 80 行 | 约 120 行(但功能更丰富) |
| 调用方式 | StringUtils.isEmpty(str) |
str.isNullOrEmpty() |
| 空安全 | 需要手动检查 null | 编译器自动处理 |
| 可读性 | 一般 | 优秀(更接近自然语言) |
| 扩展性 | 需要修改工具类 | 可以随时添加扩展函数 |
| 兼容性 | 仅 Java | Java + Kotlin(@JvmStatic) |
Kotlin 版本的优势:
- 调用更自然 :
str.isEmail()比StringUtils.isEmail(str)更直观 - 空安全 :
String?.类型系统保证空安全 - 易于扩展:可以随时为任何类添加扩展函数
- 代码组织:相关功能可以按类组织,而不是全部放在工具类中
本章小结
核心要点回顾
-
Kotlin 的核心优势
- 空安全机制:从编译时杜绝 NPE
- 代码简洁性:数据类、扩展函数等语法糖
- 与 Java 100% 互操作:增量迁移可行
- Google 官方推荐:Android 开发的未来
-
Kotlin 基础语法
valvsvar:优先使用不可变变量- 函数定义:支持默认参数、命名参数、可变参数
- 类与继承:
open关键字、override关键字 - 接口与抽象类:接口定义行为,抽象类定义模板
-
Kotlin 特有语法糖
- 数据类(data class):自动生成常用方法
- 密封类(sealed class):受限的类继承结构
- 对象表达式与单例:
object关键字 - 扩展函数与属性:不修改原类添加新功能
-
实战案例
- 用 Kotlin 扩展函数重写 Java 工具类
- Android 密度转换工具类的 Kotlin 实现
- SharedPreferences 操作的简化