Why does the Elvis operator/null coalescing operator work this way in Kotlin?

214 views Asked by At

I have the following code:

   companion object {
        private const val DEFAULT_LANGUAGE_CODE = "en-us"
    }

   val currentLanguageCode: String
   get() {
        return selectedLanguage?.code ?: configManager.get().agency?.language ?: DEFAULT_LANGUAGE_CODE
    }

Which returns null to the caller accessing currentLanguageCode..
Note, selectedLanguage is null and configManager.get().agency is null as well at that point. The following code works correctly though:

    val currentLanguageCode: String
    get() {
        val selectedCode = selectedLanguage?.code
        val configCode = configManager.get().agency?.language
        return selectedCode ?: configCode ?: DEFAULT_LANGUAGE_CODE
    }

I end up getting the DEFAULT_LANGUAGE_CODE "en-us" returned to the caller when accessing currentLanguageCode in the exact same scenario otherwise.

Any ideas why this is?

Using Kotlin 1.8.0

I checked converting this to bytecode -> Java to see if I could spot the problem but I think should have worked based on what I got:

  //working version:
   public final String getCurrentLanguageCode() {
      CommunityLanguage var10000 = this.selectedLanguage;
      String selectedCode = var10000 != null ? var10000.getCode() : null;
      ConfigAgency var3 = ((CaptureConfig)this.configManager.get()).getAgency();
      String configCode = var3 != null ? var3.getLanguage() : null;
      String var4 = selectedCode;
      if (selectedCode == null) {
         var4 = configCode;
      }

      if (var4 == null) {
         var4 = "en-us";
      }

      return var4;
   }


   //broken version which returns null
   public final String getCurrentLanguageCode() {
      String var1;
      label18: {
         CommunityLanguage var10000 = this.selectedLanguage;
         if (var10000 != null) {
            var1 = var10000.getCode();
            if (var1 != null) {
               break label18;
            }
         }

         ConfigAgency var2 = ((CaptureConfig)this.configManager.get()).getAgency();
         var1 = var2 != null ? var2.getLanguage() : null;
      }

      if (var1 == null) {
         var1 = "en-us";
      }

      return var1;
   }
1

There are 1 answers

0
Leonardo Sibela On

For me, it returns "en-us":

fun main() {
    print(test().currentLanguageCode)
}

class test {
    companion object {
        private const val DEFAULT_LANGUAGE_CODE = "en-us"
    }

    val selectedLanguage: SelectLanguage? = null
    val configManager: ConfigManager = ConfigManager()

    val currentLanguageCode: String
        get() {
            return selectedLanguage?.code ?: configManager.get().agency?.language ?: DEFAULT_LANGUAGE_CODE
        }
}

class SelectLanguage {
    val code: String = ""
}

class ConfigManager {
    fun get(): Foo {
        return Foo()
    }
}

class Foo {
    val agency: Agency? = null
}

class Agency {
    val language: String = ""
}