2

While I was playing around with enums I created this:

enum class ElectricPart {
    // Wow, what I had implemented here? Is it good or bad?!
    ;
    enum class VisionLight(val id:String){
        LIGHTS_OFF("lights_off"),
        POSITION_LIGHTS("lights_position"),
        DRIVING_LIGHTS("lights_driving"),
        LONG_RANGE_LIGHTS("lights_long_range"),
        LONG_RANGE_SIGNAL_LIGHTS("lights_long_range_signal")
    }
    enum class DirectionLight(val id:String){
        DIRECTION_LIGHTS_RIGHT("lights_direction_right"),
        DIRECTION_LIGHTS_LEFT("lights_direction_left"),
        DIRECTION_LIGHTS_STRAIGHT("lights_direction_straight")
    }
    //More enum classes if needed
}

A car has electric parts (ElectricPart). The VisionLights and DirectionLights are part of the electrics of a car.

Is this a good implementation? Is this bad? For sure it feels weird!
I would like to hear your comments about this one!

5
  • 1
    If you don't know what you created, it's bad Commented Apr 23, 2019 at 8:00
  • 2
    @TimCastelijns As I said I was playing around and I did not type random characters! I did it on purpose and I am wondering if this is a good way to go! Is it a good practice to use nested enum classes? And why it seems that it needs ; before a new enum class? Commented Apr 23, 2019 at 8:08
  • Is it a good practice to use nested enum classes? - probably not. I can't think of a situation where you would use it (or how) Commented Apr 23, 2019 at 8:17
  • 1
    If you do something you should have a reason to do it. We can't say if it's good or bad in a void. What's the purpose that it is accomplishing? What are you trying to convey with that structure? Commented Apr 23, 2019 at 8:20
  • I edited and I tried to be more specific. This is a simple idea/example I was thinking about. Commented Apr 23, 2019 at 8:34

3 Answers 3

3

You need to think about how these enums will be used by external code. In essence you are creating another namespace. The first namespace is the package, and the second is the class. That may become tedious for developers to type. It also requires the class to be typed in first for your IDE autocompletion to kick in.

Unless the package is saturated with too many definitions, or there are potential name conflicts, it might be better to define them outside of the class. Kotlin allows the enums to still be defined in the same file as the class if that helps keep you organized on the physical level.

Finally, if these enums will be used on their own, outside of the class, or shared by other classes, definitely do not nest them.

Sign up to request clarification or add additional context in comments.

Comments

1

According to your code, there is no reason for ElectricPart to be an enum class -- an enum without enumerators is pointless.

For namespacing purposes, ElectricPart might as well be a class (with private constructor, i.e. no instances) or an object (exactly one singleton instance). Note that classes are not primarily designed to be used as namespaces in Kotlin, i.e. they come at the cost of JVM runtime overhead, even if you only use their name at compile time.

A car has electric parts (ElectricPart). The VisionLights and DirectionLights are part of the electrics of a car.

This sounds like a typical is-a relationship: vision lights and direction lights are electrical parts of a car. This relation is typically modeled through inheritance and interface implementation, as such you might do this:

interface ElectricPart
enum class VisionLight(val id:String) : ElectricPart {...}
enum class DirectionLight(val id:String) : ElectricPart {...}

Keep in mind that interfaces are primarily useful when you have methods that are overridden in each implementation, to achieve polymorphic behavior. Nevertheless, it's also possible to use marker interfaces (interfaces without methods) in places where you would otherwise have to use the less type-safe Any.

Comments

0

If you desire compile-time exhaustiveness for ElectricPart, VisionLight and DirectionLight, then you could consider using a sealed interface that is implemented by both enum classes.

sealed interface ElectricPart

enum class VisionLight(val id: String) : ElectricPart {
    LIGHTS_OFF("lights_off"),
    POSITION_LIGHTS("lights_position"),
    DRIVING_LIGHTS("lights_driving"),
    LONG_RANGE_LIGHTS("lights_long_range"),
    LONG_RANGE_SIGNAL_LIGHTS("lights_long_range_signal")
}

enum class DirectionLight(val id: String) : ElectricPart {
    DIRECTION_LIGHTS_RIGHT("lights_direction_right"),
    DIRECTION_LIGHTS_LEFT("lights_direction_left"),
    DIRECTION_LIGHTS_STRAIGHT("lights_direction_straight")
}

Then you can do exhaustive whens on any ElectricPart:

fun example(part: ElectricPart) =
  // No else -> block necessary
  when (part) {
    VisionLight.LIGHTS_OFF -> TODO()
    VisionLight.POSITION_LIGHTS -> TODO()
    VisionLight.DRIVING_LIGHTS -> TODO()
    VisionLight.LONG_RANGE_LIGHTS -> TODO()
    VisionLight.LONG_RANGE_SIGNAL_LIGHTS -> TODO()
    DirectionLight.DIRECTION_LIGHTS_RIGHT -> TODO()
    DirectionLight.DIRECTION_LIGHTS_LEFT -> TODO()
    DirectionLight.DIRECTION_LIGHTS_STRAIGHT -> TODO()
  }

Or on the individual enums that implement the sealed interface:

fun example1(light: VisionLight) =
  // No else -> block necessary
  when (light) {
    VisionLight.LIGHTS_OFF -> TODO()
    VisionLight.POSITION_LIGHTS -> TODO()
    VisionLight.DRIVING_LIGHTS -> TODO()
    VisionLight.LONG_RANGE_LIGHTS -> TODO()
    VisionLight.LONG_RANGE_SIGNAL_LIGHTS -> TODO()
  }

fun example2(light: DirectionLight) =
  // No else -> block necessary
  when (light) {
    DirectionLight.DIRECTION_LIGHTS_RIGHT -> TODO()
    DirectionLight.DIRECTION_LIGHTS_LEFT -> TODO()
    DirectionLight.DIRECTION_LIGHTS_STRAIGHT -> TODO()
  }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.