0

In Swift you can write something like:

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"

In this way if a struct or class conforms to protocol Constants it has to implement something pertaining to StringConstants

Is there something like this in Kotlin which forces this kind of conformance from an interface?

It would be ideal to do something like:

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
    Why do you want an enum based getter instead of regular fun-based getter that would work in Kotlin? – Enselic Oct 04 '19 at 03:55

1 Answers1

-1

In Kotlin (as well as in Java) enums are defined at compile time as singletons. That's why you can't use variables on them. Only predefined static values.

In you have this enum

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

And you call it

val myEnum = MyEnum.MyValue

Each time you will have the same object.

All enums that are declared inside other classes are static (here is the question on stackoverflow about static keyword), which means they don't depend on objects at runtime but on classes at compile time.

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

Will compile to:

public class MyClass {

   public static enum MyEnum {
      ...
   }
}

This is why you can't use inner modifier on them:

inner enum class is not allowed

To achieve your desired result you have two options:

1) Use properties:

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) Define two different enums (without interface):

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")
    }
}

This way you can use it "as is" but without your Constants interface

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

Edit:

Kotlin supports extensions but it doesn't modify your class. Instead new method with your object as argument is created:

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

Will compile to

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

Edit 2:

I stumbled upon your question again, thought it through again, and I think I have a solution for you: how Adapter and ViewHolder are tied together in RecyclerView android lib.

interface Adapter<T : Adapter.ViewHolder> {

    fun doSmth(viewHolder: T)

    interface ViewHolder
}

Whenever you want to implement Adapter you must also implement ViewHolder:

class AdapterImpl : Adapter<AdapterImpl.MyViewHolder> {

    override fun doSmth(viewHolder: MyViewHolder) {
    }

    class MyViewHolder : Adapter.ViewHolder {
    }
}

I hope this is what you're been looking for!

GV_FiQst
  • 1,487
  • 14
  • 30
  • Thanks for the response but this is a very very basic use case you've presented there is a specific reason why I am writing a contract of `Constants` in `Swift` because I lumsum different value types together check if they conform to `Constants` and pull out their registered constant enum identifier – SwiftyKotlin Oct 04 '19 at 17:12
  • Post some code on how exactly you use it. Because from what you say I think the first option should work for you: ```listOfYourObjects.mapNonNull { it as? Constants }.forEach { val name = it.text }``` – GV_FiQst Oct 05 '19 at 12:28
  • @SwiftyKotlin I added alternative answer. Please see at `Edit 2`. Just for the sake of argument look at it, because I'm curious if I understood your question correctly. – GV_FiQst Oct 16 '19 at 15:43