我有一个关于处理接口的问题. 当我try 以这种方式使用接口时,出现错误.

当我try 以这种方式实现该接口时,我得到了一个错误,即setter不能被标记为"私有". 当在类中实现变量时,如何实现变量在接口中,可以用方法更改,并且变量本身不能从外部更改?

可以在接口中使用"val"声明变量,然后在类中将其作为"var"采用,但这样就不可能实现接口中的方法的标准实现.

如果这在原则上是不可能的,那么什么是符合OOP的正确解决方案?

非常感谢您的帮助!

interface Product{
    var count: Int

    fun add(i: Int) {
        count += 1
    }

    fun remove(i: Int) {
        count -= i
    }
}

class Salami(count: Int) : Produkt {
    override var count: Int = count
        private set // Error
}

推荐答案

问得好!原则上,您要寻找的可见性修改器称为protected.

// NOTE: Non-working code; see below for explanation
interface Product {
    var count: Int
        protected set

    fun add(i: Int) {
        count += i
    }

    fun remove(i: Int) {
        count -= i
    }
}

class Salami(count: Int) : Product {
    override var count: Int = count
        protected set // Error
}

如果这样做有效,它将显示"count可以公开读取,但只能从Product及其子类赋值",这听起来就是您想要的.

遗憾的是,JVM(扩展名为Kotlin)不允许在接口中使用protected修饰符.出现这种情况的原因在other-places中已经讨论过了,但是不管是什么原因,它现在都不会起作用,这使得您想要编写的设计模式很难实现.

因此,我们只能求助于较旧的Java样式模式.你可以随时将你的界面设置为abstract class(带有所有随之而来的限制).

abstract class Product(count: Int) {
    var count: Int = count
      private set

    fun add(i: Int) {
        count += i
    }

    fun remove(i: Int) {
        count -= i
    }
}

class Salami(count: Int) : Product(count) {}

但现在调用者必须扩展类,而不是实现接口,这限制了他们只能进行单继承.

相反,您可以使用抽象实现模式,在接口中有默认实现之前,Swing等标准Java库就使用该模式.使用这种模式,您的interface Product是完全抽象的,并且没有默认实现.但随后您提供了一个抽象类AbstractProduct,它整齐地实现了所有方法.如果需要扩展另一个类,调用者仍然可以自由地实现方法themselves,但大多数调用者只会扩展AbstractProduct.在你的情况下,应该是这样的.

interface Product {
    val count: Int
    fun add(i: Int): Unit
    fun remove(i: Int): Unit
}

abstract class AbstractProduct(count: Int) : Product {
    // Note: final modifier is required here since we have a private
    // setter, which cannot be open.
    final override var count: Int = count
      private set

    override fun add(i: Int) {
      count += i
    }

    override fun remove(i: Int): Unit {
      count -= i
    }

}

现在,大多数应用程序都会是这样的.

class Salami(count: Int) : AbstractProduct(count) {}

但是,如果Salami需要扩展Meat或其他类,它可以自己这样做.

class Salami(count: Int) : Meat(), Product {
    final override var count: Int = count
      private set

    override fun add(i: Int) {
      count += i
    }

    override fun remove(i: Int): Unit {
      count -= i
    }
}

因此,如果需要,用户仍然可以获得完整的通用性,但在简单的情况下,一切都会正常工作.它不是完美的,但它接近你想要的.只需记住,产品应该始终使用Product个函数taking,而不是AbstractProduct个函数,因为后者是一个实现细节.

Composition over Inheritance

最后,您可以总是偏爱组合而不是继承.你得到的这个count设置是它自己的封装的子系统,所以我们可以明确地说明这一点.


class ProductCount(initialValue: Int) {

    var value: Int = initialValue
        private set

    fun add(i: Int): Unit {
      value += i
    }

    fun remove(i: Int): Unit {
      value -= i
    }

}

interface Product {
  val productCount: ProductCount
}

class Salami(count: Int) : Product {
  override val productCount = ProductCount(count)
}

现在,only,ProductCount人可以修改计数.除了提供给它的addremove方法之外,甚至连Product都不能做到这一点.我们已经将产品的数量与产品本身脱钩.而且,如果您愿意,您甚至可以在product上提供委托方法,在ProductCount上调用相同的方法,以使API看起来更像您原来的API.

interface Product {
  val productCount: ProductCount

  fun add(i: Int): Unit = productCount.add(i)
  fun remove(i: Int): Unit = productCount.remove(i)
  val count: Int
      get = productCount.value
}

现在,您的公共API完全符合您的要求.你有一个count的整数变量,不能从外部修改,你有addremove个方法,do可以修改它.所有这些都被封装在一个ProductCount类中,这一事实对您的API用户来说是隐藏得相当好的.

Kotlin相关问答推荐

如何在Kotlin中反射多个对象以查找特定类型的属性

判断字符串是否除了.&" ",","@""""

kotlin 模式匹配如何像 scala 一样工作

generic 类实例列表 - 调用采用 T 的函数

Kotlin .如何用从 1 到 90 的 5 个唯一数字填充列表中的每一行?

ActivityResultContracts TakePicture 结果总是返回 false

如何使用 Firebase 和 Kotlin 在文本 (Jetpack Compose) 中显示当前用户名?

在子类中覆盖 kotlin 运算符扩展函数

Mixin 在 Jackson 中添加 defaultImpl 不起作用

Kotlin RxJava 可空的错误

jetpack compose 将参数传递给 viewModel

使用最新的 com.jakewharton.rxbinding3:rxbinding:3.0.0-alpha2 库时未找到 RxTextView 和其他小部件

将 Completable 转换为 Single 的规范方法?

Android 与 Kotlin - 如何使用 HttpUrlConnection

在kotlin中初始化类变量的正确位置是什么

Kotlin:相互递归函数的尾部递归

Lint 错误:可疑的相等判断:在 Object DiffUtilEquals 中未实现 equals()

为什么 Kotlin 会收到这样的 UndeclaredThrowableException 而不是 ParseException?

Kotlin中对象和数据类的区别是什么?

使用 Kotlin 按字母对数组进行排序