Home > Software engineering >  Kotlin - How to return object based on condition
Kotlin - How to return object based on condition

Time:10-07

Let's say I have two objects in Kotlin:

// IOSIdentifiers.kt
object IOSIdentifiers {
   const val STRING_1 = "some_ios_specific_identifier_string1"
   const val STRING_2 = "some_ios_specific_identifier_string2"
}

// IOSIdentifiers.kt
object AndroidIdentifiers {
   const val STRING_1 = "some_android_specific_identifier_string1"
   const val STRING_2 = "some_android_specific_identifier_string2"
}

// Identifiers.kt
object Identifiers {
   // Pseudo code:
   if(platform == "iOS") IOSIdentifiers else AndroidIdentifiers
}

and I want to use them like so: someFunction(Identifiers.STRING_1), how I can do it?

I tried this:

// Identifiers.kt
object Identifiers {
   val IDENTIFIERS
        get() = if (platform == "iOS") IOSIdentifiers else AndroidIdentifiers
}

but then I need to do someFunction(Identifiers.IDENTIFIERS.STRING_1) which is not what I want.

I also tried this:

// Identifiers.kt
val Identifiers
        get() = if (Configuration.isIOS) IOSIdentifiers else AndroidIdentifiers

but then I lost typings, because val Identifiers is Any type.

CodePudding user response:

If you are not allowed to modify the declaration of the 2 identifier objects, it will be more verbose because Kotlin's type system is nominal (and you can't retroactively implement an interface after declaring a class). But you can use delegation like this:

val platform="iOS"
// IOSIdentifiers.kt
object IOSIdentifiers {
    const val STRING_1 = "some_ios_specific_identifier_string1"
    const val STRING_2 = "some_ios_specific_identifier_string2"
}

// IOSIdentifiers.kt
object AndroidIdentifiers {
    const val STRING_1 = "some_android_specific_identifier_string1"
    const val STRING_2 = "some_android_specific_identifier_string2"
}
object Identifier{
    val STRING_1: String
        get() = if (platform== "iOS") IOSIdentifiers.STRING_1 else AndroidIdentifiers.STRING_1
    val STRING_2: String
        get() = if (platform== "iOS") IOSIdentifiers.STRING_2 else AndroidIdentifiers.STRING_2
}

fun main() {
    print(Identifier.STRING_1)
    print(Identifier.STRING_2)
}

On the other hand if you can modify the declaration, then you can declare an abstract class that declares both String fields and make the objects extend them, like this:

object AndroidIdentifiers : IIdentifiers() {
    override val STRING_1 = "some_android_specific_identifier_string1"
    override val STRING_2 = "some_android_specific_identifier_string2"
}
object IOSIdentifiers : IIdentifiers(){
    override val STRING_1 = "some_ios_specific_identifier_string1"
    override val STRING_2 = "some_ios_specific_identifier_string2"
}

abstract class IIdentifiers {
    abstract val STRING_1 : String
    abstract val STRING_2 : String
}

val Identifiers = if (platform== "iOS") IOSIdentifiers else AndroidIdentifiers
}

Another way is to use interface delegation (almost the same idea but use interface instead): https://kotlinlang.org/docs/delegation.html

val platform= "iOS"
val delegate= if (platform== "iOS") IOSIdentifiers else AndroidIdentifiers

// IOSIdentifiers.kt
object AndroidIdentifiers : IIdentifiers {
    override val STRING_1 = "some_android_specific_identifier_string1"
    override val STRING_2 = "some_android_specific_identifier_string2"
}
object IOSIdentifiers : IIdentifiers{
    override val STRING_1 = "some_ios_specific_identifier_string1"
    override val STRING_2 = "some_ios_specific_identifier_string2"
}

interface IIdentifiers {
    val STRING_1 : String
    val STRING_2 : String
}

object Identifier : IIdentifiers by delegate

CodePudding user response:

You can make a common interface. For succinctness, you can give that interface a companion object that implements the self-same interface and use delegation to pick the right one. This requires that the platform is set on Configuration before this companion object is ever referenced.

object IOSIdentifiers: Identifiers {
    override val string1 = "some_ios_specific_identifier_string1"
    override val string2 = "some_ios_specific_identifier_string2"
}

object AndroidIdentifiers: Identifiers {
    override val string1 = "some_android_specific_identifier_string1"
    override val string2 = "some_android_specific_identifier_string2"
}

interface Identifiers {
    val string1: String
    val string2: String

    companion object: Identifiers by when (Configuration.platform) {
        "IOS" -> IOSIdentifiers
        "Android" -> AndroidIdentifiers
        else -> error("Unknown platform ${Configuration.platform}")
    }
}

But if this is a multi-platform project, you can use expect/actual to do this is a more sensible way.

expect class Identifiers {
    val string1: String
    val string2: String
}

// In iOS module:
actual class Identifiers {
    actual val string1 = "some_ios_specific_identifier_string1"
    actual val string2 = "some_ios_specific_identifier_string2"
}

// In Android module:
actual class Identifiers {
    actual val string1 = "some_android_specific_identifier_string1"
    actual val string2 = "some_android_specific_identifier_string2"
}
  • Related