簡體   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