Android开发中Kotlin语言的优势:为什么它是现代开发者的首选?

在Android开发领域,编程语言的选择直接影响着开发效率、代码质量和应用性能。Kotlin作为Google官方推荐的Android开发语言,已经从2017年的实验性支持发展到2024年成为绝大多数开发者的首选。本文将深入探讨Kotlin在Android开发中的核心优势,通过详细的对比和实际代码示例,帮助你理解为什么现代开发者纷纷转向Kotlin。
1. 空安全(Null Safety):告别NullPointerException
1.1 Kotlin的空安全机制
空指针异常(NullPointerException,简称NPE)是Android开发中最常见也最令人头疼的错误。根据Google的统计,超过90%的Android应用崩溃都与NPE有关。Kotlin通过其类型系统从根本上解决了这个问题。
// Java中的空指针风险
public class User {
public String name; // 可能为null
public String email; // 可能为null

public void printEmail() {
// 可能抛出NullPointerException
System.out.println(email.toUpperCase());
}
}

// Kotlin的空安全声明
class User {
var name: String? = null // 可为空类型
var email: String? = null // 可为空类型

fun printEmail() {
// 编译错误:需要安全调用或非空断言
// println(email.uppercase()) // 这行代码无法编译

// 安全调用方式1:使用?.操作符
println(email?.uppercase()) // 如果email为null,返回null

// 安全调用方式2:使用Elvis操作符提供默认值
println(email?.uppercase() ?: “NO_EMAIL”)

// 安全调用方式3:非空断言(不推荐,可能导致NPE)
// println(email!!.uppercase())

// 安全调用方式4:if判断
if (email != null) {
println(email.uppercase()) // 智能转换为非空类型
}
}
}
1.2 实际应用场景
// 在Android开发中常见的场景
class UserProfileActivity : AppCompatActivity() {
private var userName: String? = null

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// 安全获取Intent数据
userName = intent?.getStringExtra(“USER_NAME”)

// 安全更新UI
findViewById(R.id.tv_name)?.text = userName ?: “Guest”

// 安全处理网络响应
val user = fetchUserFromApi()
user?.let { safeUser ->
// 在这个代码块中,safeUser是非空的
updateUI(safeUser)
}
}

private fun fetchUserFromApi(): User? {
// 模拟可能返回null的网络请求
return if (System.currentTimeMillis() % 2 == 0L) User() else null
}

private fun updateUI(user: User) {
// 这里的user保证非空
println(“Updating UI for user: ${user.name}”)
}
}
1.3 与Java的对比
// Java代码 – 需要大量null检查
public String getUserName(User user) {
if (user != null) {
if (user.profile != null) {
if (user.profile.name != null) {
return user.profile.name;
}
}
}
return “Unknown”;
}

// Kotlin代码 – 简洁且安全
fun getUserName(user: User?): String {
return user?.profile?.name ?: “Unknown”
}
2. 扩展函数:增强现有类的功能
2.1 基本概念
Kotlin允许开发者在不继承现有类的情况下为其添加新功能,这在Android开发中极为有用。
// 为String类添加扩展函数
fun String.toSlug(): String {
return this.toLowerCase()
.replace(Regex(“\\s+”), “-“)
.replace(Regex(“[^a-z0-9-]”), “”)
}

// 为View类添加扩展函数
fun View.show() {
this.visibility = View.VISIBLE
}

fun View.hide() {
this.visibility = View.GONE
}

fun View.toggleVisibility() {
this.visibility = if (this.visibility == View.VISIBLE) View.GONE else View.VISIBLE
}

// 在Activity中使用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val textView = findViewById(R.id.tv_hello)

// 使用扩展函数
textView.hide()
textView.show()
textView.toggleVisibility()

// 字符串扩展
val title = “Hello World From Kotlin”
val slug = title.toSlug() // “hello-world-from-kotlin”
println(slug)
}
}
2.2 实际开发中的高级扩展
// 为Context添加扩展函数
fun Context.toast(message: String, duration: Int = Toast.LENGTH_SHORT) {
Toast.makeText(this, message, duration).show()
}

fun Context.isNetworkAvailable(): Boolean {
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val network = connectivityManager.activeNetwork
val networkCapabilities = connectivityManager.getNetworkCapabilities(network)
return networkCapabilities?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
}

// 为LiveData添加扩展函数
fun LiveData.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer) {
observe(lifecycleOwner, object : Observer {
override fun onChanged(value: T) {
removeObserver(this)
observer.onChanged(value)
}
})
}

// 为Bitmap添加扩展函数
fun Bitmap.scaleTo(targetWidth: Int, targetHeight: Int): Bitmap {
return Bitmap.createScaledBitmap(this, targetWidth, targetHeight, true)
}

fun Bitmap.toBase64(): String {
val byteArray = ByteArrayOutputStream()
this.compress(Bitmap.CompressFormat.PNG, 100, byteArray)
return Base64.encodeToString(byteArray.toByteArray(), Base64.DEFAULT)
}
3. 数据类(Data Classes):简化数据模型
3.1 基本用法
// Java中定义POJO类需要大量模板代码
public class User {
private String id;
private String name;
private String email;
private int age;

// 构造函数
public User(String id, String name, String email, int age) {
this.id = id;
this.name = name;
this.email = email;
this.age = age;
}

// Getter和Setter
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }

// equals, hashCode, toString都需要手动实现
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(id, user.id) &&
Objects.equals(name, user.name) && Objects.equals(email, user.email);
}

@Override
public int hashCode() {
return Objects.hash(id, name, email, age);
}

@Override
public String toString() {
return “User{id=’” + id + “‘, name=’” + name + “‘, email=’” + email + “‘, age=” + age + “}”;
}
}

// Kotlin中只需一行代码
data class User(
val id: String,
var name: String,
var email: String,
var age: Int
)

// 使用示例
fun main() {
val user1 = User(“1”, “Alice”, “alice@example.com”, 25)
val user2 = User(“1”, “Alice”, “alice@example.com”, 25)

// 自动生成的equals方法
println(user1 == user2) // true

// 自动生成的toString方法
println(user1) // User(id=1, name=Alice, email=alice@example.com, age=25)

// 自动生成的copy方法
val user3 = user1.copy(name = “Bob”)
println(user3) // User(id=1, name=Bob, email=alice@example.com, age=25)

// 解构声明
val (id, name, email, age) = user1
println(“ID: $id, Name: $name”) // ID: 1, Name: Alice
}
3.2 在Android中的实际应用
// API响应数据类
data class ApiResponse(
val success: Boolean,
val data: T?,
val message: String? = null,
val timestamp: Long = System.currentTimeMillis()
)

// 用户数据类
data class User(
val id: String,
val username: String,
val email: String,
val profilePicUrl: String? = null,
val isActive: Boolean = true
)

// RecyclerView的数据类
data class ProductItem(
val id: String,
val name: String,
val price: Double,
val imageUrl: String,
val isInStock: Boolean = true
) {
// 在数据类中添加方法
fun getFormattedPrice(): String = “$${“%.2f”.format(price)}”

fun shouldShowDiscount(): Boolean = price > 100.0
}

// 在ViewModel中使用
class UserViewModel : ViewModel() {
private val _users = MutableLiveData>()
val users: LiveData> get() = _users

fun loadUsers() {
// 模拟API调用
val userList = listOf(
User(“1”, “Alice”, “alice@example.com”),
User(“2”, “Bob”, “bob@example.com”)
)
_users.value = userList
}

fun updateUser(user: User) {
val currentList = _users.value ?: emptyList()
val newList = currentList.map { existingUser ->
if (existingUser.id == user.id) user else existingUser
}
_users.value = newList
}
}
4. 协程(Coroutines):简化异步编程
4.1 协程基础
// 传统回调方式 – 嵌套地狱
fun loadUserData(userId: String, callback: (User?) -> Unit) {
fetchUserFromDatabase(userId) { user ->
if (user != null) {
fetchUserPosts(user.id) { posts ->
if (posts != null) {
fetchUserComments(posts.map { it.id }) { comments ->
// 最终回调
callback(user)
} else {
callback(null)
}
} else {
callback(null)
}
}
} else {
callback(null)
}
}
}

// 协程方式 – 线性代码
suspend fun loadUserData(userId: String): User? = withContext(Dispatchers.IO) {
try {
val user = fetchUserFromDatabase(userId)
if (user != null) {
val posts = fetchUserPosts(user.id)
val comments = fetchUserComments(posts.map { it.id })
// 处理数据…
return@withContext user
}
} catch (e: Exception) {
// 异常处理
Log.e(“UserLoad”, “Failed to load user”, e)
}
return@withContext null
}
4.2 Android中的实际应用
class UserRepository(private val api: UserApi, private val db: UserDatabase) {

// 简单的网络请求
suspend fun refreshUser(userId: String): User {
return withContext(Dispatchers.IO) {
val user = api.getUser(userId)
db.userDao().insert(user)
user
}
}

// 并行执行多个请求
suspend fun refreshUserWithPosts(userId: String): UserWithPosts {
return withContext(Dispatchers.IO) {
// 同时启动两个协程
val userDeferred = async { api.getUser(userId) }
val postsDeferred = async { api.getUserPosts(userId) }

// 等待两个请求都完成
val user = userDeferred.await()
val posts = postsDeferred.await()

UserWithPosts(user, posts)
}
}

// 带超时的请求
suspend fun fetchUserWithTimeout(userId: String): User? {
return withTimeoutOrNull(5000) { // 5秒超时
api.getUser(userId)
}
}

// 重试机制
suspend fun fetchUserWithRetry(userId: String): User {
var lastException: Exception? = null

repeat(3) { attempt ->
try {
return api.getUser(userId)
} catch (e: Exception) {
lastException = e
if (attempt < 2) delay(1000 * (attempt + 1)) // 指数退避 } } throw lastException ?: Exception("Unknown error") } } // 在ViewModel中使用 class UserViewModel(private val repository: UserRepository) : ViewModel() { private val _userState = MutableStateFlow(UserState.Loading)
val userState: StateFlow = _userState.asStateFlow()

// viewModelScope是LifecycleScope的ViewModel版本
fun loadUser(userId: String) {
viewModelScope.launch {
_userState.value = UserState.Loading

try {
val user = repository.refreshUser(userId)
_userState.value = UserState.Success(user)
} catch (e: Exception) {
_userState.value = UserState.Error(e.message ?: “Unknown error”)
}
}
}

// 处理多个并发请求
fun loadDashboard(userId: String) {
viewModelScope.launch {
try {
val (user, posts, stats) = repository.refreshUserWithPosts(userId)
_userState.value = UserState.Dashboard(user, posts, stats)
} catch (e: Exception) {
_userState.value = UserState.Error(e.message ?: “Unknown error”)
}
}
}
}

// 状态密封类
sealed class UserState {
object Loading : UserState()
data class Success(val user: User) : UserState()
data class Dashboard(val user: User, val posts: List, val stats: Stats) : UserState()
data class Error(val message: String) : UserState()
}
4.3 协程与RxJava对比
// RxJava方式
fun loadUserRx(userId: String): Single {
return api.getUser(userId)
.flatMap { user ->
api.getUserPosts(user.id)
.map { posts -> UserWithPosts(user, posts) }
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
}

// 协程方式
suspend fun loadUserCoroutines(userId: String): UserWithPosts {
return withContext(Dispatchers.IO) {
val user = api.getUser(userId)
val posts = api.getUserPosts(user.id)
UserWithPosts(user, posts)
}
}

// 优势对比:
// 1. 协程代码更线性,易于理解和调试
// 2. 协程没有额外的依赖(RxJava需要引入库)
// 3. 协程的异常处理更符合Kotlin语言特性
// 4. 协程可以更容易地与现有代码集成
5. 智能类型转换(Smart Casts)
5.1 基本概念
// Java中需要显式类型转换
public void processObject(Object obj) {
if (obj instanceof String) {
String str = (String) obj; // 需要显式转换
System.out.println(str.toUpperCase());
} else if (obj instanceof Integer) {
Integer num = (Integer) obj; // 需要显式转换
System.out.println(num * 2);
}
}

// Kotlin自动进行类型转换
fun processObject(obj: Any) {
when (obj) {
is String -> {
// 自动转换为String类型
println(obj.uppercase())
}
is Int -> {
// 自动转换为Int类型
println(obj * 2)
}
is List<*> -> {
// 自动转换为List
println(“List size: ${obj.size}”)
}
else -> {
println(“Unknown type”)
}
}
}
5.2 在Android中的实际应用
// 处理View点击事件
fun setupClickListeners() {
button1.setOnClickListener { view ->
// 自动转换为Button类型
view.text = “Clicked”
}

// 处理不同类型的View
container.setOnClickListener { view ->
when (view) {
is Button -> {
// 自动转换为Button
view.text = “Button clicked”
view.setTextColor(Color.RED)
}
is TextView -> {
// 自动转换为TextView
view.append(” (clicked)”)
}
else -> {
view.visibility = View.GONE
}
}
}
}

// 处理Intent数据
fun handleIntent(intent: Intent?) {
when (intent?.action) {
Intent.ACTION_VIEW -> {
// 自动转换为Uri类型
val data = intent.data
if (data != null) {
handleUri(data)
}
}
Intent.ACTION_SEND -> {
// 自动转换为String类型
val text = intent.getStringExtra(Intent.EXTRA_TEXT)
if (text != null) {
handleSharedText(text)
}
}
}
}

// 处理RecyclerView ViewHolder
class UserViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val nameTextView: TextView = view.findViewById(R.id.tv_name)
private val emailTextView: TextView = view.findViewById(R.id.tv_email)

fun bind(user: User) {
nameTextView.text = user.name
emailTextView.text = user.email

// 智能转换处理点击
itemView.setOnClickListener {
// 自动转换为View类型
when (it) {
is TextView -> {
// 如果点击的是TextView
it.text = “Selected: ${user.name}”
}
else -> {
// 如果点击的是其他View
Toast.makeText(it.context, “User: ${user.name}”, Toast.LENGTH_SHORT).show()
}
}
}
}
}
6. 函数式编程特性
6.1 高阶函数与Lambda表达式
// 高阶函数定义
fun calculate(a: Int, b: Int, operation: (Int, Int) -> Int): Int {
return operation(a, b)
}

// 使用示例
fun main() {
val sum = calculate(5, 3) { x, y -> x + y }
val product = calculate(5, 3) { x, y -> x * y }
val max = calculate(5, 3) { x, y -> maxOf(x, y) }

println(“Sum: $sum, Product: $product, Max: $max”)
}
6.2 集合操作
// 传统方式处理集合
fun processUsers(users: List): List {
val result = mutableListOf()
for (user in users) {
if (user.isActive && user.age >= 18) {
result.add(user.name.uppercase())
}
}
return result
}

// 函数式方式
fun processUsersFunctional(users: List): List {
return users
.filter { it.isActive && it.age >= 18 }
.map { it.name.uppercase() }
.sorted()
}

// 在Android中的实际应用
class UserRepository {
fun getActiveAdultUsers(): List {
return allUsers.filter { it.isActive && it.age >= 18 }
}

fun getUserEmails(): List {
return allUsers.map { it.email }
}

fun findUserById(id: String): User? {
return allUsers.find { it.id == id }
}

fun groupUsersByAge(): Map> {
return allUsers.groupBy { it.age }
}

fun partitionUsers(): Pair, List> {
return allUsers.partition { it.isActive }
}
}
6.3 Scope Functions(作用域函数)
// let函数 – 用于空安全执行
fun processUser(user: User?) {
user?.let { safeUser ->
// 只在user不为null时执行
println(“Processing user: ${safeUser.name}”)
// safeUser在这里是非空的
}
}

// run函数 – 对象配置
fun createConfig(): Config {
return Config().run {
baseUrl = “https://api.example.com”
timeout = 30
retries = 3
this // 返回配置对象
}
}

// apply函数 – 对象初始化
fun createUser(name: String, email: String): User {
return User().apply {
this.name = name
this.email = email
createdAt = System.currentTimeMillis()
isActive = true
}
}

// also函数 – 额外操作
fun saveUser(user: User) {
user.also {
// 在保存前做一些额外操作
println(“Saving user: ${it.name}”)
analytics.trackEvent(“user_save”, mapOf(“userId” to it.id))
}.saveToDatabase()
}

// with函数 – 多个操作同一对象
fun setupView(view: View) {
with(view) {
visibility = View.VISIBLE
setBackgroundColor(Color.WHITE)
setPadding(16, 16, 16, 16)
// 可以直接访问view的方法
}
}

// 在Android中的综合应用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

// 使用apply初始化View
val textView = TextView(this).apply {
text = “Hello Kotlin”
textSize = 18f
setTextColor(Color.BLACK)
gravity = Gravity.CENTER
layoutParams = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT
)
}

// 使用let处理可能为null的数据
intent?.getStringExtra(“KEY”)?.let { value ->
// 只在不为null时执行
textView.text = value
}

// 使用run配置对象
val dialog = AlertDialog.Builder(this).run {
setTitle(“Alert”)
setMessage(“This is a message”)
setPositiveButton(“OK”) { _, _ -> /* handle */ }
setNegativeButton(“Cancel”) { _, _ -> /* handle */ }
create()
}

// 使用also进行额外操作
dialog.show().also {
// 显示后记录日志
Log.d(“Dialog”, “Dialog shown at ${System.currentTimeMillis()}”)
}
}
}
7. 字符串模板与原始字符串
7.1 字符串模板
// 传统字符串拼接
val name = “Alice”
val age = 25
val message = “User ” + name + ” is ” + age + ” years old”

// 字符串模板
val messageTemplate = “User $name is $age years old”

// 复杂表达式
val user = User(“1”, “Alice”, “alice@example.com”, 25)
val userInfo = “User ${user.name} (${user.email}) is ${user.age} years old”

// 在Android中的应用
class UserViewHolder(view: View) {
private val textView: TextView = view.findViewById(R.id.tv_info)

fun bind(user: User) {
textView.text = “””
Name: ${user.name}
Email: ${user.email}
Age: ${user.age}
Status: ${if (user.isActive) “Active” else “Inactive”}
“””.trimIndent()
}
}
7.2 原始字符串
// 传统字符串需要转义
val json = “{\”name\”:\”Alice\”,\”age\”:25,\”email\”:\”alice@example.com\”}”

// 原始字符串
val jsonRaw = “””
{
“name”: “Alice”,
“age”: 25,
“email”: “alice@example.com”
}
“””.trimIndent()

// SQL查询
val query = “””
SELECT
u.id,
u.name,
u.email
FROM users u
WHERE u.active = 1
ORDER BY u.name
“””.trimIndent()

// HTML模板
val htmlTemplate = “””


User Profile

${user.name}

Email: ${user.email}

Age: ${user.age}



“””.trimIndent()
8. 密封类(Sealed Classes)与模式匹配
8.1 密封类基础
// 密封类用于表示受限的类继承结构
sealed class Result {
data class Success(val data: T) : Result()
data class Error(val exception: Exception) : Result()
object Loading : Result()
}

// 使用密封类处理状态
class DataViewModel : ViewModel() {
private val _result = MutableLiveData>>()
val result: LiveData>> = _result

fun loadData() {
_result.value = Result.Loading

viewModelScope.launch {
try {
val users = api.getUsers()
_result.value = Result.Success(users)
} catch (e: Exception) {
_result.value = Result.Error(e)
}
}
}
}

// 在UI中处理
class MainActivity : AppCompatActivity() {
private fun observeResult(liveData: LiveData>>) {
liveData.observe(this) { result ->
when (result) {
is Result.Loading -> {
showLoading()
}
is Result.Success -> {
hideLoading()
showUsers(result.data)
}
is Result.Error -> {
hideLoading()
showError(result.exception.message ?: “Unknown error”)
}
}
}
}
}
8.2 实际应用示例
// UI事件密封类
sealed class UiEvent {
data class Login(val username: String, val password: String) : UiEvent()
data class Register(val user: User) : UiEvent()
data class UpdateProfile(val profile: Profile) : UiEvent()
object Logout : UiEvent()
}

// 网络请求密封类
sealed class NetworkResult {
data class Success(val data: T) : NetworkResult()
data class Error(val code: Int, val message: String) : NetworkResult()
data class Exception(val throwable: Throwable) : NetworkResult()
}

// 处理网络请求
class UserRepository {
suspend fun getUser(userId: String): NetworkResult {
return try {
val response = api.getUser(userId)
if (response.isSuccessful) {
response.body()?.let {
NetworkResult.Success(it)
} ?: NetworkResult.Error(404, “User not found”)
} else {
NetworkResult.Error(response.code(), response.message())
}
} catch (e: Exception) {
NetworkResult.Exception(e)
}
}
}
9. 委托(Delegation)
9.1 属性委托
// 延迟初始化委托
class UserProfileActivity : AppCompatActivity() {
// 使用by lazy延迟初始化
private val userName: String by lazy {
// 只在第一次访问时执行
intent?.getStringExtra(“USER_NAME”) ?: “Guest”
}

private val userApi: UserApi by lazy {
// 只在第一次使用时创建
Retrofit.Builder()
.baseUrl(“https://api.example.com/”)
.build()
.create(UserApi::class.java)
}

// 可观察属性委托
private var userCount: Int by Delegates.observable(0) { prop, old, new ->
// 当值改变时执行
println(“User count changed from $old to $new”)
updateUI()
}

// 约束属性委托
private var age: Int by Delegates.vetoable(0) { prop, old, new ->
// 可以阻止值的改变
new in 0..120 // 只允许0-120的值
}
}

// 自定义委托
class PreferenceDelegate(
private val context: Context,
private val key: String,
private val defaultValue: T
) {
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
val prefs = context.getSharedPreferences(“APP_PREFS”, Context.MODE_PRIVATE)
return when (defaultValue) {
is String -> prefs.getString(key, defaultValue) as T
is Int -> prefs.getInt(key, defaultValue) as T
is Boolean -> prefs.getBoolean(key, defaultValue) as T
else -> throw UnsupportedOperationException(“Type not supported”)
}
}

operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
val prefs = context.getSharedPreferences(“APP_PREFS”, Context.MODE_PRIVATE)
with(prefs.edit()) {
when (value) {
is String -> putString(key, value)
is Int -> putInt(key, value)
is Boolean -> putBoolean(key, value)
}
apply()
}
}
}

// 使用自定义委托
class Settings(private val context: Context) {
var username: String by PreferenceDelegate(context, “username”, “”)
var notificationsEnabled: Boolean by PreferenceDelegate(context, “notifications”, true)
var theme: Int by PreferenceDelegate(context, “theme”, 0)
}
9.2 类委托
// 使用by关键字实现委托模式
class UserRepository(private val api: UserApi) : UserApi by api {
// 可以重写某些方法
override suspend fun getUser(userId: String): User {
// 添加额外逻辑
Log.d(“UserRepository”, “Fetching user $userId”)
return api.getUser(userId)
}
}

// 这样就不需要手动实现所有UserApi的方法
interface UserApi {
suspend fun getUser(userId: String): User
suspend fun getUsers(): List
suspend fun updateUser(user: User): Boolean
}

// 在Android中的应用
class CachedUserRepository(
private val api: UserApi,
private val cache: UserCache
) : UserApi by api {

override suspend fun getUser(userId: String): User {
// 先检查缓存
cache.getUser(userId)?.let { return it }

// 缓存未命中,调用API
val user = api.getUser(userId)
cache.saveUser(user)
return user
}
}
10. 扩展属性
10.1 基本用法
// 为View添加扩展属性
var View.visible: Boolean
get() = visibility == View.VISIBLE
set(value) {
visibility = if (value) View.VISIBLE else View.GONE
}

// 为String添加扩展属性
val String.isEmail: Boolean
get() = Patterns.EMAIL_ADDRESS.matcher(this).matches()

val String.isPhone: Boolean
get() = Patterns.PHONE.matcher(this).matches()

// 为List添加扩展属性
val List.second: T?
get() = if (size >= 2) this[1] else null

val List.third: T?
get() = if (size >= 3) this[2] else null

// 在Android中的应用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

val editText = findViewById(R.id.et_email)
val button = findViewById