Есть ли в Kotlin способ для интерфейса иметь связанный тип контракта? - PullRequest
0 голосов
/ 04 октября 2019

В Swift вы можете написать что-то вроде:

protocol Constants {
    associatedtype StringProperties
}

struct ViewState {
    let viewName: String
}

extension ViewState: Constants {

    enum StringProperties: String {
        case text = "some view text"
    }

}

struct LayerState {
    let layerName: String
}

extension LayerState: Constants {

    enum StringProperties: String {
        case text = "some layer text"
    }
}

let viewState = ViewState(viewName: "Some view name")
let viewValue = ViewState.StringProperties.text.rawValue // "some view text"

let layerState = LayerState(layerName: "Some layer name")
let layerValue = LayerState.StringProperties.text.rawValue // "some layer text"

Таким образом, если структура или класс соответствуют протоколу Constants, он должен реализовать что-то, относящееся к StringConstants

Есть ли что-то подобное в Kotlin, которое вызывает такое соответствие через интерфейс?

Было бы идеально сделать что-то вроде:

interface Constants {
    enum class StringProperties 
}

data class ViewState(val viewName: String): Constants {

    enum class StringProperties(val string: String) {
        Text("some view text")
    }

}

data class LayerState(val layerName: String): Constants {

    enum class StringProperties(val string: String) {
        Text("some layer text")
    }

}

val viewState = ViewState("some view name")
val viewText = ViewState.StringProperties.Text.string // "some view text"

val layerState = LayerState("some layer name")
val layerText = LayerState.StringProperties.Text.string // "some layer text"

1 Ответ

0 голосов
/ 04 октября 2019

В Kotlin (как и в Java) перечисления определяются во время компиляции как одиночные. Вот почему вы не можете использовать переменные на них. Только предопределенные статические значения.

У вас есть это перечисление

enum class MyEnum(val text: String) {
    MyValue("some text")
}

И вы называете его

val myEnum = MyEnum.MyValue

Каждый раз, когда у вас будет один и тот же объект.

Все перечисления, которые объявлены внутри других классов, являются static (здесь * это вопрос по stackoverflow о статическом ключевом слове), что означает, что они не зависят от объектов во время выполнения, но отклассы во время компиляции.

data class MyClass {
   enum class MyEnum {
       ...
   }
}

Будет компилироваться в:

public class MyClass {

   public static enum MyEnum {
      ...
   }
}

Вот почему вы не можете использовать inner модификатор для них:

inner enum class is not allowed

Для достижения желаемого результата у вас есть два варианта:

1) Использовать свойства:

interface Constants {
   val text: String
}

// data classes are final and cannot be extended later
data class ViewState(val viewName: String) : Constants {
    override val text = "some view text"
}

data class LayerState(val layerName: String) : Constants {
    override val text = "some layer text"
}

2) Определитьдва разных перечисления (без интерфейса):

data class ViewState(val viewName: String) {
    enum class StringProperties(val string: String) {
        Text("some view text")
    }
}

data class LayerState(val layerName: String) {
    enum class StringProperties(val string: String) {
        Text("some layer text")
    }
}

Таким образом, вы можете использовать его "как есть", но без вашего Constants интерфейса

val viewText = ViewState.StringProperties.Text.string // "some view text"
val layerText = LayerState.StringProperties.Text.string // "some layer text"

Редактировать:

Kotlin поддерживает extensions , но не меняет ваш класс. Вместо этого создается новый метод с вашим объектом в качестве аргумента:

fun MyObject.extensionFunc() {
   // do stuff
}

Скомпилируется в

public void extensionFunc(MyObject $receiver) {
    // do stuff
}

Редактировать 2:

Я наткнулся наВаш вопрос снова, продумал его снова, и я думаю, что у меня есть решение для вас: как Adapter и ViewHolder связаны в RecyclerView android lib.

interface Adapter<T : Adapter.ViewHolder> {

    fun doSmth(viewHolder: T)

    interface ViewHolder
}

Всякий раз, когда вы хотитереализовать Adapter вы также должны реализовать ViewHolder:

class AdapterImpl : Adapter<AdapterImpl.MyViewHolder> {

    override fun doSmth(viewHolder: MyViewHolder) {
    }

    class MyViewHolder : Adapter.ViewHolder {
    }
}

Я надеюсь, это то, что вы искали!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...