【问题标题】:Using a when clause in an enum class getter in Kotlin在 Kotlin 的枚举类 getter 中使用 when 子句
【发布时间】:2018-05-09 22:01:20
【问题描述】:

我正在尝试根据调用它的枚举的值来获取 kotlin 中属性的特定 getter。这是我到目前为止得到的:

enum class Endpoint {
    EVENTS, GAMES;

    val baseUrl = "https://www.example.com/api"

    val path: String
        get() = when(this){
            EVENTS -> "$baseUrl/events"
            GAMES -> "$baseUrl/games"
        }
}

这样称呼:

print(Endpoint.EVENTS.path)

虽然编译没有任何问题,但一旦我运行它,我就会得到一个 NullPointerException 错误 Attempt to invoke virtual method 'java.lang.Object [...].Endpoint[].clone()' on a null object reference

我不确定我做错了什么或完成上述操作的正确方法是什么。

编辑:异常的完整日志:

05-09 22:51:33.793 15673-15673/com.filippovigani.eventvods E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.filippovigani.eventvods, PID: 15673
java.lang.ExceptionInInitializerError
    at com.filippovigani.eventvods.networking.Endpoint.getPath(Endpoint.kt:21)
    at com.filippovigani.eventvods.networking.Endpoint.<init>(Endpoint.kt:25)
    at com.filippovigani.eventvods.networking.Endpoint.<clinit>(Endpoint.kt)
    at com.filippovigani.eventvods.networking.EventvodsApi$Companion.getEvents(EventvodsApi.kt:8)
    at com.filippovigani.eventvods.MainActivity.onCreate(MainActivity.kt:19)
    at android.app.Activity.performCreate(Activity.java:5990)
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
    at android.app.ActivityThread.access$800(ActivityThread.java:151)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:135)
    at android.app.ActivityThread.main(ActivityThread.java:5254)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
 Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object com.filippovigani.eventvods.networking.Endpoint[].clone()' on a null object reference
    at com.filippovigani.eventvods.networking.Endpoint.values(Endpoint.kt)
    at com.filippovigani.eventvods.networking.Endpoint$WhenMappings.<clinit>(Unknown Source)
    at com.filippovigani.eventvods.networking.Endpoint.getPath(Endpoint.kt:21) 
    at com.filippovigani.eventvods.networking.Endpoint.<init>(Endpoint.kt:25) 
    at com.filippovigani.eventvods.networking.Endpoint.<clinit>(Endpoint.kt) 
    at com.filippovigani.eventvods.networking.EventvodsApi$Companion.getEvents(EventvodsApi.kt:8) 
    at com.filippovigani.eventvods.MainActivity.onCreate(MainActivity.kt:19) 
    at android.app.Activity.performCreate(Activity.java:5990) 
    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106) 
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278) 
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387) 
    at android.app.ActivityThread.access$800(ActivityThread.java:151) 
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303) 
    at android.os.Handler.dispatchMessage(Handler.java:102) 
    at android.os.Looper.loop(Looper.java:135) 
    at android.app.ActivityThread.main(ActivityThread.java:5254) 
    at java.lang.reflect.Method.invoke(Native Method) 
    at java.lang.reflect.Method.invoke(Method.java:372) 
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) 
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698) 

【问题讨论】:

  • 你能确认你正在使用的 Kotlin/Gradle/JVM 的版本吗,我测试了你的代码,它工作正常
  • @jrtapsell kotlin_version = '1.2.41',gradle_version = '3.1.2'。不确定这是否重要,但我正在使用 android 数据绑定库进行开发。
  • 我用1.2.41试过了,还是不能重现错误
  • 在 TIO Try it online! 上测试
  • 这很有趣。我已经添加了完整的异常日志,也许它可以帮助查明问题所在。

标签: enums kotlin switch-statement getter


【解决方案1】:

我无法重现您的错误,代码工作正常。尽管如此,我认为解决方案有点过于复杂,为什么不使用构造函数参数来提供特定于常量的值:

enum class Endpoint(service: String) {
    EVENTS("/events"), GAMES("/games");

    private val baseUrl = "https://www.example.com/api"
    val path: String = baseUrl + service
}

【讨论】:

  • 你说得对,我的解决方案比你的要复杂得多。谢谢。
  • 这是完成您所寻找的正确方法。枚举类的值是类的实例,因此可以由构造函数初始化,甚至可以实现它们自己的匿名类。您可以在此处阅读有关 Kotlin 枚举的更多信息:kotlinlang.org/docs/reference/enum-classes.html
【解决方案2】:

异常表明您正试图在枚举的构造函数中访问path。这会导致问题,因为枚举在构造期间尚未准备好使用。

这意味着以下调用链会导致失败:

  1. 初始化Endpoint
  2. 初始化Endpoint.EVENTS
  3. 致电Endpoint.getPath()(这不会显示在您的代码中)
  4. 使用Endpoint$WhenMapping,开始该类的初始化。
  5. Endpoint$WhenMapping 使用 EndPoint.values(),但由于我们仍在初始化枚举的实例,因此无法提供 values 数组,返回 null
  6. 为确保不被修改,WhenMapping 会克隆并缓存该数组,但由于该数组在任何时候都不应该在初始化之外时为空,这会导致 NPE。

简单地说,您的代码以某种方式依赖于一个需要完全初始化才能使用的类。由于您的代码没有显示您如何调用Endpoint.EVENTS.path,所以只能这样说。

【讨论】:

  • 我真的很难理解它。我调用Endpoint.EVENTS.path 的方式是在活动的onCreate 方法中使用print(Endpoint.EVENTS.path)(日志有些不同,但出于测试目的,我只是进行了打印调用)。然而,正如其他人所说,代码对他们来说很好。不知道我错过了什么。
猜你喜欢
  • 2019-06-17
  • 1970-01-01
  • 2021-01-17
  • 2021-07-20
  • 1970-01-01
  • 1970-01-01
  • 2015-09-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多