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 接口的默认实现提供了强大的灵活性,使得接口不仅可以定义契约,还可以提供通用行为,促进了代码重用和可扩展性。
相关推荐
AFinalStone15 小时前
Android 7系统异常问题排查(四)Framework层(上)—System Server Watchdog
android·系统异常
宝杰X716 小时前
Android Room3 多平台数据库
android·数据库
huibin14785236919 小时前
热缓解学习记录
android·学习
小田的博客1 天前
SAP MM 供应商银行主数据更新报错!message R1228!
android·java·服务器
一笑的小酒馆1 天前
Android智能猫砂盆视频加载慢卡顿问题分析
android
只会cv的小前端1 天前
七巧低代码服务端脚本使用方法
android·低代码·rxjava
1 天前
Android 自定义 View 实战:从零打造工业级全向摇杆控件(OmniJoystickView)
android·kotlin·自定义view·摇杆控件
雨白1 天前
深入理解 Kotlin 协程 (十一):以逸待劳,探秘 select 多路复用与并发安全策略
android·kotlin
hunterandroid1 天前
[Android 从零到一] Android 深度链接与 App Links:从 URI Scheme 到可验证的应用跳转
android
我命由我123451 天前
Jetpack Compose - Material Design 断点范围、WindowSizeClass、针对不同屏幕尺寸创建预览、四类导航栏
android·java·开发语言·java-ee·kotlin·android jetpack·android runtime