【问题标题】:Getting Platform declaration clash when using Interface in kotlin [duplicate]在kotlin中使用接口时获取平台声明冲突[重复]
【发布时间】:2018-01-12 14:20:00
【问题描述】:

我正在将 Java 中的一些类转换为 kotlin,但在尝试从接口继承时遇到了编译错误:

平台声明冲突:以下声明具有相同的 JVM 签名(getContentID()Ljava/lang/String;):

public open fun get-content-id(): 字符串? public open fun getContentId(): String?

界面如下:

interface Media {
  val contentId: String

  val displayRunTime: String

  val genres: List<String>

  val programId: String

  val runTime: String

  val type: String
}

这是课程:

class Airing : Media {
  override val contentId: String? = null
  override val displayRunTime: String? = null
  override val genres: List<String>? = null
  override val programId: String? = null
  override val runTime: String? = null
  override val type: String? = null

  override fun getContentId(): String? {
    return contentId
  }

我是 kotlin 的超级新手。

【问题讨论】:

  • 你在你的接口Media中定义了变量或函数吗?看起来它们是变量(Kotlin 中的属性)。我想你想定义一个函数列表(Java 中的方法)并在Airling 类中实现它们。没事吧?

标签: java android kotlin


【解决方案1】:

您不需要声明override fun getContentId(): String?,因为来自Media 接口的val contentId: String 已经被override val contentId: String? 覆盖。

您收到的错误意味着您在 JVM 字节码中声明的函数与已为 contentId 属性生成的 getter 冲突(该 getter 具有相同的签名)。

在 Kotlin 中,您应该直接使用属性 contentId,而在 Java 中,您可以使用生成的访问器 getContentId()setContentId(...)

此外,Kotlin 不允许您使用可空的 String? 覆盖非空 String 属性,因为基本接口的用户会期望该属性的非空值。您应该将覆盖的属性类型替换为String,或者在界面中将它们设置为String?

【讨论】:

  • 还有String属性覆盖String?属性的问题。
【解决方案2】:

主要问题是T? 类型包括Tnull。反过来,TT? 的子集。所以你不能用它的超集类型覆盖T 属性,例如:

interface Source { val content: Any }

//                               v--- ERROR: `Any?` is not a subtype of `Any`
class Image(override val content:Any?) : Source

但是,您可以使用其子集类型子类型覆盖T?属性,例如:

interface Source { val content: Any? }

//                              v--- `Any` is a subset of `Any?`
class Image(override val content:Any) : Source

// `String` is a subtype of `Any` & `Any?` includes `Any`
//                               v
class Text(override val content:String) : Source

那么您的代码应该如下所示。然而,Kotlin 是一个空安全系统,你不能在不初始化的情况下定义一个属性,即使该属性是 nullable 属性:

class Airing : Media {
   //           v--- ERROR: the property isn't initialized      
   override val contentId: String
   ...
}

你应该在主构造函数中引入属性,让客户端代码在未来初始化它们,例如:

class Airing
constructor(
        override val contentId: String,
        override val displayRunTime: String,
        override val genres: List<String>,
        override val programId: String,
        override val runTime: String,
        override val type: String
) : Media 

您还可以为属性提供一些默认值,例如:

class Airing : Media {
    override val contentId: String = "<default>"
    override val displayRunTime: String = "<default>"
    override val genres: List<String> = emptyList()
    override val programId: String = "<default>"
    override val runTime: String = "<default>"
    override val type: String = "<default>"
}

AND getContentId 函数会与 getter 冲突,所以你应该删除它。

【讨论】:

  • 注意:此规则适用于不可变的val 属性,因为覆盖方法可以为其返回类型提供Java 中的子类型。换句话说,您不能在可变的var 属性中应用该规则。
【解决方案3】:

声明为val 的变量类似于Java 的final 变量,是不可变的,因此您不能设置为Optional ?

你的变量声明应该如下:

interface Media {
    var contentId: String?
    //...
}

【讨论】:

    猜你喜欢
    • 2018-02-09
    • 1970-01-01
    • 2018-11-23
    • 2017-11-17
    • 1970-01-01
    • 2019-02-15
    • 1970-01-01
    • 2012-10-10
    • 2021-08-11
    相关资源
    最近更新 更多