2

So after I created lots of enum classes in android (Kotlin), I learnt that enums are not very space efficient in android. So I tried to find another way of doing it. I found the enumerated annotation - @StringDef. However, Kotlin doesn't seem to support this well and there are no warning or error messages even if I pass something unexpected to a method.

So to clarify what I want to do: I have tons of constant strings that can be classified to different groups (I listed them in different enum class), and when calling some setter functions, I want the caller to choose only from the specific group of things that can be chosen.

For example:

enum class Cat (val breed: String){
    AMER_SHORTHAIR("American Shorthair"),
    SIAMESE("Siamese");
}
enum class Dog (val breed: String){
    GOLDEN_R("Golden Retriever"),
    POODLE("Poodle");
}
fun adopt(cat: Cat, dog: Dog){
    print("I adopted a "+cat.breed+" and a "+dog.breed)
}

In this case, I can only choose from cats for the first param, and dogs for the second. Is there a way of doing this kind of type-safe methods without using enums?

To avoid using enums, I might need to change the above functionality to:

const val AMER_SHORTHAIR = "American Shorthair"
const val SIAMESE = "Siamese"
const val GOLDEN_R = "Golden Retriever"
const val POODLE = "Poodle"
fun adopt(cat: String, dog: String){...}

This is not ideal since we can get all kinds of typos which happens in our current implementation and is why I switched to enum in the first place. But overall, space consumption > type safe. So if there is no better way, I will need to switch back.

Please let me know if there is any efficient ways to achieve. I've thought about using maps or lists, but indexing or accessing the strings become cumbersome because I need to map the string to themselves (no hard coded strings here except for the first assignment like AMER_SHORTHAIR = "American Shorthair").

Thanks.

ljzhanglc
  • 507
  • 2
  • 8
  • 11
  • 1
    `learnt that enums are not very space efficient in android.` oh boy, that was relevant like, 7 years ago. You seem to have missed the flamewar. Just use enums. Enums are just as "space-efficient" as any class you make, yet we don't start removing classes and making 10000 line methods. – EpicPandaForce Aug 01 '18 at 01:16

2 Answers2

1

I agree with @EpicPandaForce comment: you should not optimize Enums in this way.

That said, there will be a new feature: Inline Classes coming in Kotlin 1.3 (https://blog.jetbrains.com/kotlin/2018/07/see-whats-coming-in-kotlin-1-3-m1/).

With this new feature you could do the following:

inline data class Cat(private val breed: String)
inline data class Dog(private val breed: String)

val AMER_SHORTHAIR = Cat("American Shorthair");
val SIAMESE = Cat("Siamese");

val GOLDEN_R = Dog("Golden Retriever");
val POODLE = Dog("Poodle");  

fun adopt(cat: Cat, dog: Dog){
    print("I adopted a "+cat.breed+" and a "+dog.breed)
} 

Whenever you use this values the compiler inlines to raw strings.

In contrast to type aliases, the compiler is able to check the origin types for inline classes.

Rene
  • 5,730
  • 17
  • 20
0

I like to use Sealed class instead of Enums; It provides some extra goodies over the normal class; More on this here

Something like this;

class LoggingBehaviour @JvmOverloads constructor(private val logLevel: LogLevel = Debug) {
    fun log(tag: String, message: String) {
        when (logLevel) {
            is Error -> Log.e(tag, message)
            is Warn -> Log.w(tag, message)
            is Info -> Log.i(tag, message)
            is Debug -> Log.d(tag, message)
            is Verbose -> Log.v(tag, message)
        }
    }
}

sealed class LogLevel
object Error : LogLevel()
object Warn : LogLevel()
object Info : LogLevel()
object Debug : LogLevel()
object Verbose : LogLevel()

Also annotations like IntDef and StringDef can be used. More of it here

Vishal Ambre
  • 403
  • 4
  • 9