2-2-2 快速掌握Kotlin-语言的接口默认实现

Kotlin 接口的默认实现详解

Kotlin 接口的默认实现是 Kotlin 与 Java 8+ 都支持的特性,但 Kotlin 的实现更加灵活和强大。下面是详细解析:

1. 基本默认实现

kotlin 复制代码
interface Printer {
    // 抽象方法(必须实现)
    fun print(document: String)
    
    // 默认实现方法(可选重写)
    fun preview(document: String) {
        println("预览文档: $document")
    }
    
    // 带返回值的默认实现
    fun getDefaultPageSize(): Int {
        return 210 to 297  // Pair 类型
    }
}

class LaserPrinter : Printer {
    // 只必须实现抽象方法
    override fun print(document: String) {
        println("激光打印: $document")
    }
    
    // 可以选择重写默认方法
    override fun preview(document: String) {
        println("激光打印机预览: $document")
    }
}

2. 默认实现的不同形式

表达式体默认实现

kotlin 复制代码
interface Calculator {
    // 使用表达式体的默认实现
    fun add(a: Int, b: Int): Int = a + b
    fun subtract(a: Int, b: Int): Int = a - b
    
    // 复杂默认实现
    fun calculate(expression: String): Int {
        return when {
            expression.contains("+") -> {
                val nums = expression.split("+")
                nums[0].trim().toInt() + nums[1].trim().toInt()
            }
            expression.contains("-") -> {
                val nums = expression.split("-")
                nums[0].trim().toInt() - nums[1].trim().toInt()
            }
            else -> 0
        }
    }
}

使用其他接口方法

kotlin 复制代码
interface Shape {
    fun area(): Double
    fun perimeter(): Double
    
    // 默认实现可以调用抽象方法
    fun description(): String {
        return "面积: ${area()}, 周长: ${perimeter()}"
    }
}

class Circle(val radius: Double) : Shape {
    override fun area(): Double = Math.PI * radius * radius
    override fun perimeter(): Double = 2 * Math.PI * radius
    // 使用默认的 description() 实现
}

3. 属性默认实现

kotlin 复制代码
interface User {
    // 抽象属性(必须实现)
    val username: String
    
    // 带 getter 的默认实现属性
    val displayName: String
        get() = "User: $username"
    
    // 带 setter 的默认实现(Kotlin 1.6.20+)
    var email: String
        get() = "$username@example.com"
        set(value) {
            // 可以在这里添加验证逻辑
            require(value.contains("@")) { "Invalid email" }
            // 但注意:接口属性没有 backing field,不能直接存储值
        }
}

class Member(override val username: String) : User {
    // 提供存储空间
    private var _email: String = "$username@default.com"
    
    override var email: String
        get() = _email
        set(value) {
            require(value.contains("@")) { "Invalid email" }
            _email = value
        }
}

4. 默认实现中的多继承冲突解决

kotlin 复制代码
interface A {
    fun foo() {
        println("A.foo")
    }
    
    fun bar()
}

interface B {
    fun foo() {
        println("B.foo")
    }
    
    fun bar() {
        println("B.bar")
    }
}

// 实现类必须解决冲突
class C : A, B {
    // foo() 在两个接口都有默认实现,必须重写
    override fun foo() {
        // 选择调用特定接口的实现
        super<A>.foo()
        super<B>.foo()
        println("C.foo")
    }
    
    // bar() 只在 B 中有默认实现,可以这样处理:
    override fun bar() {
        super<B>.bar()  // 调用 B 的实现
        // 或者提供完全不同的实现
    }
}

更复杂的冲突解决

kotlin 复制代码
interface Clickable {
    fun click() = println("Clickable clicked")
    fun showOff() = println("我是可点击的!")
}

interface Focusable {
    fun focus() = println("获得焦点")
    fun showOff() = println("我是可聚焦的!")
}

class Button : Clickable, Focusable {
    override fun click() {
        super.click()
        println("按钮被点击")
    }
    
    // 必须重写 showOff() 解决冲突
    override fun showOff() {
        // 明确指定调用哪个父接口的实现
        super<Clickable>.showOff()
        super<Focusable>.showOff()
    }
}

5. 默认实现中的私有方法和属性

kotlin 复制代码
interface Logger {
    fun log(message: String)
    
    // 公共默认方法
    fun logInfo(message: String) {
        logWithTimestamp("[INFO] $message")
    }
    
    fun logError(message: String) {
        logWithTimestamp("[ERROR] $message")
    }
    
    // 私有辅助方法(Kotlin 1.7+)
    private fun logWithTimestamp(message: String) {
        val timestamp = java.time.LocalDateTime.now()
        log("[$timestamp] $message")
    }
}

class ConsoleLogger : Logger {
    override fun log(message: String) {
        println(message)
    }
}

// 使用
val logger = ConsoleLogger()
logger.logInfo("系统启动")  // 自动加上时间戳

6. 接口默认实现与抽象类的区别

kotlin 复制代码
// 接口:不能存储状态,但可以有默认实现
interface Repository {
    fun save(data: String)
    fun delete(id: String)
    
    // 接口可以有默认实现
    fun exists(id: String): Boolean {
        return load(id) != null
    }
    
    fun load(id: String): String?  // 抽象方法
}

// 抽象类:可以有状态和构造函数
abstract class AbstractRepository {
    // 可以有状态(存储字段)
    protected val cache = mutableMapOf<String, String>()
    
    // 可以有构造函数
    constructor(name: String) {
        println("初始化仓库: $name")
    }
    
    // 抽象方法
    abstract fun save(data: String)
    
    // 具体方法(需要 open 才能被重写)
    open fun delete(id: String) {
        cache.remove(id)
    }
    
    // 可以有 final 方法(不能被重写)
    fun clearCache() {
        cache.clear()
    }
}

7. 默认实现的进阶用法

为现有接口添加扩展

kotlin 复制代码
interface Listener {
    fun onEvent(event: String)
}

// 为接口添加默认实现(实际上是为实现类添加扩展)
fun Listener.onEventWithDefault() {
    onEvent("default event")
}

class MyListener : Listener {
    override fun onEvent(event: String) {
        println("收到事件: $event")
    }
}

// 使用
val listener = MyListener()
listener.onEventWithDefault()  // 调用扩展函数

模板方法模式

kotlin 复制代码
interface DataProcessor {
    // 模板方法:定义算法骨架
    fun process(data: String): String {
        val validated = validate(data)
        val transformed = transform(validated)
        return finalize(transformed)
    }
    
    // 可重写的步骤
    open fun validate(data: String): String {
        require(data.isNotBlank()) { "数据不能为空" }
        return data.trim()
    }
    
    abstract fun transform(data: String): String
    
    open fun finalize(data: String): String {
        return "$data [已处理]"
    }
}

class UppercaseProcessor : DataProcessor {
    override fun transform(data: String): String {
        return data.uppercase()
    }
    
    // 可以选择重写其他步骤
    override fun validate(data: String): String {
        return super.validate(data).take(100)  // 限制长度
    }
}

8. Java 互操作性

Kotlin 接口的默认实现在 Java 中的表现

kotlin 复制代码
// Kotlin 接口
interface KInterface {
    fun required()
    fun optional() {
        println("默认实现")
    }
}

// Java 中使用
public class JavaClass implements KInterface {
    @Override
    public void required() {
        System.out.println("必须实现");
    }
    
    // 可选:重写 optional() 或不重写
    @Override
    public void optional() {
        // 调用默认实现
        KInterface.DefaultImpls.optional(this);
        // 或者提供自己的实现
    }
}

使用 @JvmDefault 注解(Kotlin 1.2-1.3)

kotlin 复制代码
// 旧版本兼容性
interface OldInterface {
    @JvmDefault
    fun defaultMethod() {
        println("兼容 Java 8 之前的默认方法")
    }
}

9. 默认实现的限制和注意事项

kotlin 复制代码
interface LimitedInterface {
    // 注意:接口不能有构造函数
    // constructor() {}  // 错误!
    
    // 不能有初始化块
    // init { println("初始化") }  // 错误!
    
    // 不能有幕后字段(field)
    var value: String
        get() = "default"
        set(value) {
            // field = value  // 错误!接口不能有 field
        }
    
    // 解决方法:使用抽象属性 + 默认 getter/setter
    abstract var realValue: String
    
    var delegatedValue: String
        get() = realValue
        set(value) { realValue = value }
}

10. 实用设计模式

组合代替继承

kotlin 复制代码
interface Behavior {
    fun act()
}

class Walking : Behavior {
    override fun act() = println("走路")
}

class Flying : Behavior {
    override fun act() = println("飞行")
}

class Swimming : Behavior {
    override fun act() = println("游泳")
}

// 使用接口组合
class Duck {
    private val behaviors = mutableListOf<Behavior>()
    
    fun addBehavior(behavior: Behavior) {
        behaviors.add(behavior)
    }
    
    fun perform() {
        behaviors.forEach { it.act() }
    }
}

// 配置不同的行为组合
val duck = Duck()
duck.addBehavior(Walking())
duck.addBehavior(Swimming())
duck.perform()

策略模式

kotlin 复制代码
interface DiscountStrategy {
    fun calculate(price: Double): Double
}

class NoDiscount : DiscountStrategy {
    override fun calculate(price: Double): Double = price
}

class PercentageDiscount(val percentage: Double) : DiscountStrategy {
    override fun calculate(price: Double): Double = price * (1 - percentage / 100)
}

class FixedDiscount(val amount: Double) : DiscountStrategy {
    override fun calculate(price: Double): Double = maxOf(0.0, price - amount)
}

class ShoppingCart(private var strategy: DiscountStrategy = NoDiscount()) {
    var total: Double = 0.0
    
    fun addItem(price: Double) {
        total += price
    }
    
    fun checkout(): Double {
        return strategy.calculate(total)
    }
    
    fun setStrategy(newStrategy: DiscountStrategy) {
        strategy = newStrategy
    }
}

11. 测试接口的默认实现

kotlin 复制代码
interface Repository {
    fun save(item: String): Boolean
    
    // 默认实现
    fun saveAll(items: List<String>): List<Boolean> {
        return items.map { save(it) }
    }
}

// 测试默认实现
@Test
fun testDefaultImplementation() {
    val mockRepository = object : Repository {
        val savedItems = mutableListOf<String>()
        
        override fun save(item: String): Boolean {
            savedItems.add(item)
            return true
        }
    }
    
    val items = listOf("a", "b", "c")
    val results = mockRepository.saveAll(items)
    
    assertEquals(3, results.size)
    assertEquals(listOf("a", "b", "c"), mockRepository.savedItems)
}

总结要点

  1. 默认实现是可选重写的:实现类可以选择使用默认实现或提供自己的实现

  2. 解决多继承冲突:当多个接口有相同签名的默认方法时,实现类必须重写并指定使用哪个

  3. 接口 vs 抽象类

    • 接口:不能存储状态,支持多继承,有默认实现
    • 抽象类:可以存储状态,单继承,可以有具体方法和抽象方法
  4. Java 互操作 :Kotlin 的默认方法在 Java 中可见为带有 DefaultImpls 内部类

  5. 私有方法:Kotlin 1.7+ 支持接口中的私有方法,用于辅助默认实现

  6. 实用模式:默认实现使得接口可以用于模板方法、策略模式等设计模式

    markdown 复制代码
                                                                                                                                                                                                                                                     Kotlin 接口的默认实现提供了强大的灵活性,使得接口不仅可以定义契约,还可以提供通用行为,促进了代码重用和可扩展性。
相关推荐
代码s贝多芬的音符2 小时前
android webview 打开相机 相册 图片上传。
android·webview·webview打开相机相册
游戏开发爱好者82 小时前
抓包工具有哪些?代理抓包、数据流抓包、拦截转发工具
android·ios·小程序·https·uni-app·iphone·webview
StarShip2 小时前
Android system_server进程介绍
android
StarShip2 小时前
Android Context 的 “上下文”
android
成都大菠萝2 小时前
2-6-1 快速掌握Kotlin-语言的接口定义
android
李小轰_Rex3 小时前
纯算法AEC:播录并行场景的回声消除实战笔记
android·音视频开发
ok406lhq3 小时前
unity游戏调用SDK支付返回游戏会出现画面移位的问题
android·游戏·unity·游戏引擎·sdk
成都大菠萝5 小时前
2-2-2 快速掌握Kotlin-函数&Lambda
android
成都大菠萝5 小时前
2-1-1 快速掌握Kotlin-kotlin中变量&语句&表达式
android