繁体   English   中英

从Kotlin实现Java接口时出现NullPointerException

[英]NullPointerException when implementing Java interface from Kotlin

我正在尝试在Kotlin中实现BiFunction接口,我得到一个NullPointerException

这是我在Kotlin中实现的Java接口。 它来自RxJava 2。

package io.reactivex.functions;

import io.reactivex.annotations.NonNull;

/**
 * A functional interface (callback) that computes a value based on multiple input values.
 * @param <T1> the first value type
 * @param <T2> the second value type
 * @param <R> the result type
 */
public interface BiFunction<T1, T2, R> {

    /**
     * Calculate a value based on the input values.
     * @param t1 the first value
     * @param t2 the second value
     * @return the result value
     * @throws Exception on error
     */
    @NonNull
    R apply(@NonNull T1 t1, @NonNull T2 t2) throws Exception;
}

这是我的实施

class MonitoringStateReducer: BiFunction<MonitoringViewState, MonitoringResult, 
    MonitoringViewState> {
    override fun apply(
        previousState: MonitoringViewState,
        result: MonitoringResult
    ): MonitoringViewState {
        when (result) {
           //Returns a non-null new state
        }
    }
}

然后,在ViewModel中,我尝试使用它,但它会抛出NullPointerException。

2019-08-22 09:57:41.049 6925-6925 / com.name.app E / AndroidRuntime:FATAL EXCEPTION:main进程:com.name.app,PID:6925 java.lang.RuntimeException:无法启动活动ComponentInfo { com.name.app/com.name.app.features.monitoring.presentation.MonitoringActivity}:java.lang.NullPointerException:在android.app的android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2907)中,accumulator为null。 Activity.Thread.handleLaunchActivity(ActivityThread.java:2986)位于android.app.Handler.dispatchMessage上的android.app.ActivityThread.-wrap11(未知来源:0)android.app.ActivityThread $ H.handleMessage(ActivityThread.java:1641) (Handler.java:105)位于android.app.Looper.loop(Looper.java:164)的android.app.ActivityThread.main(ActivityThread.java:6694),位于java.lang.reflect.Method.invoke(Native方法) )com.android.internal.os.Zygote $ MethodAndArgsCaller.run(Zygote.java:240)at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:769)引起:java.lang.NullPointerException: accumu lator在io.reactivex.internal.function.ObjectHelper.requireNonNull(ObjectHelper.java:39)处于空白处于io.reactivex.Observable.scanWith(Observable.java:11537)io.reactivex.Observable.scan(Observable.java: 11502)com的com.name.app.features.monitoring.presentation.MonitoringViewModel.compose(MonitoringViewModel.kt:47)位于com的com.name.app.features.monitoring.presentation.MonitoringViewModel。(MonitoringViewModel.kt:18)。 name.app.features.monitoring.presentation.MonitoringViewModel_Factory.get(MonitoringViewModel_Factory.java:25)位于dagger.internal.DoubleCheck的com.name.app.features.monitoring.presentation.MonitoringViewModel_Factory.get(MonitoringViewModel_Factory.java:8)。在androidx.lifecycle.ViewModelProvider的androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:164)的com.name.app.di.viewmodel.ViewModelFactory.create(ViewModelFactory.kt:12)获取(DoubleCheck.java:47) .get(ViewModelProvider.java:130)at com.name.app.features.monitoring.presentation.MonitoringActivity $ viewMo del $ 2.invoke(MonitoringActivity.kt:46)at com.name.app.features.monitoring.presentation.MonitoringActivity $ viewModel $ 2.invoke(MonitoringActivity.kt:26)at kotlin.UnsafeLazyImpl.getValue(Lazy.kt:81) at com.name.app.features.monitoring.presentation.MonitoringActivity.getViewModel(Unknown Source:7)at com.name.app.features.monitoring.presentation.MonitoringActivity.bind(MonitoringActivity.kt:85)at com.name。 app.features.monitoring.presentation.MonitoringActivity.onCreate(MonitoringActivity.kt:119)at android.app.Activity.performCreate(Activity.java:6984)at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1235)at android Android.app.ActivityThread $中android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2986)的android.app.ActivityThread.-wrap11(未知来源:0)的.app.ActivityThread.performLaunchActivity(ActivityThread.java:2860) H.handleMessage(ActivityThread.java:1641)位于android.os.Looper的android.os.Handler.dispatchMessage(Handler.java:105) .loop(Looper.java:164)位于android.app.ActivityThread.main(ActivityThread.java:6694)的java.lang.reflect.Method.invoke(Native Method)at com.android.internal.os.Zygote $ MethodAndArgsCaller com.android.internal.os.ZygoteInit.main。(Zygote.java:240)

class MonitoringViewModel @Inject constructor(
    private val processor: MonitoringProcessor
) : BaseViewModel<MonitoringIntention, MonitoringViewState>() {
    //Properties that are not relevant for the question

    private val reducer: MonitoringStateReducer = MonitoringStateReducer()

    private fun compose(): Observable<MonitoringViewState> {
        return intentsSubject.compose(intentFilter)
            .map(actionFromIntent)
            .compose(processor)
            .scan(MonitoringViewState.init(), reducer) //Exception is here
            .distinctUntilChanged()
            .replay(1)
            .autoConnect(0)
    }

    override fun state(): Observable<MonitoringViewState> = compose()

    //Functions that are not relevant for the question
}

这段代码也不起作用。

private val reducer by lazy(LazyThreadSafetyMode.NONE) {
    MonitoringStateReducer()
}

但是,如果我用这个代码替换reducer,它就可以了。

private val reducer: BiFunction<MonitoringViewState, MonitoringResult, MonitoringViewState>
    get() = MonitoringStateReducer()

在Kotlin 1.3.40和1.3.50上测试。

问题来自Kotlin类初始化顺序。 崩溃的原因是BaseViewModel构造函数正在调用在子MonitoringViewModel类中重写的state()方法。 结果,当访问reducer时,它还没有初始化。 在构造派生类的新实例期间,基类初始化作为第一步完成,并且它在派生类的初始化逻辑运行之前发生。 看一下这篇文章 ,它描述了一个非常类似的问题。 派生类初始化顺序 Kotlin的文档部分也应该是有用的。

@Valeriy Katkov的回答是正确的,请接受他的回答。

这是一个模拟相同场景的代码:

fun main() {
    MonitoringViewModel()
}

open abstract class BaseViewModel {

    init {
        state()
    }

    abstract fun state()
}

class MonitoringViewModel: BaseViewModel() {

    private val reducer1: MonitoringStateReducer = MonitoringStateReducer()
    private val reducer2: BiFunction<MonitoringViewState, MonitoringResult, MonitoringViewState>
        get() = MonitoringStateReducer()

    override fun state() {
        Observable.just(MonitoringResult(3))
                .scan(MonitoringViewState(0), reducer1)
                .subscribe { state -> println(state.value) }
    }

}

data class MonitoringViewState(val value: Int)

data class MonitoringResult(val value: Int)

class MonitoringStateReducer : BiFunction<MonitoringViewState, MonitoringResult,
        MonitoringViewState> {
    override fun apply(
            previousState: MonitoringViewState,
            result: MonitoringResult
    ): MonitoringViewState {
        return MonitoringViewState(previousState.value + result.value)
    }
}

如果使用reducer1使用scan运算符运行此代码,您将看到异常:

线程“main”中的异常java.lang.NullPointerException:accumulator为null

如果使用reducer2使用scan运算符运行代码,则成功。

使用reduce1时为什么会崩溃?

来自@Valeriy Katkov的回答

崩溃的原因是BaseViewModel构造函数正在调用在子MonitoringViewModel类中重写的state()方法。 结果,当访问reducer时,它还没有初始化。 在构造派生类的新实例期间,基类初始化作为第一步完成,并且它在派生类的初始化逻辑运行之前发生。

我怀疑堆栈跟踪的这一部分是否有答案:

 at com.name.app.features.monitoring.presentation.MonitoringViewModel_Factory.get(MonitoringViewModel_Factory.java:8) at dagger.internal.DoubleCheck.get(DoubleCheck.java:47) at com.name.app.di.viewmodel.ViewModelFactory.create(ViewModelFactory.kt:12) at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:164) 

我从中获取的是您正在使用Dagger来创建MonitoringViewModel类的实例。 不确定 Dagger会做我将要描述的内容,但我知道其他库(如Gson)这样做,它符合模式......

如果使用Unsafe类,则可以创建对象而不实际调用构造函数。 请参阅本文的“避免初始化”部分: http//mishadoff.com/blog/java-magic-part-4-sun-dot-misc-dot-unsafe/

如果这发生在这里,那么你的private val reducer = MonitoringStateReducer()将永远不会被执行,因为那是类初始化的一部分。 因此, reducer从未初始化,并且当您尝试在compose()使用它时为null

我不确定为什么在使用by lazy委托时它会使它不起作用,但是当你提供自定义get()时它确实有意义:它不再是类初始化的一部分,而是被评估一经请求。

尝试在不使用Dagger的情况下创建此类的实例,并查看“正常”初始化是否有效。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM