运行良好的 Java 代码导致 kotlin.TypeCastException:在转换为 Kotlin 代码时,无法将 null 转换为非空类型

Well working Java code is causing kotlin.TypeCastException: null cannot be cast to non-null type when converted to Kotlin code

我必须为 RecyclerView 的项目添加动画并创建一个 CustomGridRecyclerView 作为给定的打击:

public class CustomGridRecyclerView extends RecyclerView {

    public CustomGridRecyclerView(Context context) {
        super(context);
    }

    public CustomGridRecyclerView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomGridRecyclerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setLayoutManager(LayoutManager layout) {
        if (layout instanceof GridLayoutManager) {
            super.setLayoutManager(layout);
        } else {
            throw new ClassCastException("This recyclerview should use grid layout manager as the layout manager");
        }
    }

    @Override
    protected void attachLayoutAnimationParameters(View child, ViewGroup.LayoutParams params, int index, int count) {

        if (getAdapter() != null && getLayoutManager() instanceof GridLayoutManager) {

            GridLayoutAnimationController.AnimationParameters animationParams =
                    (GridLayoutAnimationController.AnimationParameters) params.layoutAnimationParameters;

            if (animationParams == null) {
                animationParams = new GridLayoutAnimationController.AnimationParameters();
                params.layoutAnimationParameters = animationParams;
            }

            int columns = ((GridLayoutManager) getLayoutManager()).getSpanCount();

            animationParams.count = count;
            animationParams.index = index;
            animationParams.columnsCount = columns;
            animationParams.rowsCount = count / columns;

            final int invertedIndex = count - 1 - index;
            animationParams.column = columns - 1 - (invertedIndex % columns);
            animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns;

        } else {
            super.attachLayoutAnimationParameters(child, params, index, count);
        }
    }
}

效果很好,但我想在另一个使用 Kotlin 的应用程序中使用以下代码实现相同的功能:

class CustomGridRecyclerView : RecyclerView {
    constructor(context: Context?) : super(context!!)
    constructor(context: Context?, attrs: AttributeSet?) : super(context!!, attrs)
    constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(
        context!!,
        attrs,
        defStyle
    )

    override fun setLayoutManager(layout: LayoutManager?) {
        if (layout is GridLayoutManager) {
            super.setLayoutManager(layout)
        } else {
            throw ClassCastException("This RecyclerView should use GridLayoutManager as the layout manager")
        }
    }

    override fun attachLayoutAnimationParameters(
        child: View?,
        params: ViewGroup.LayoutParams,
        index: Int,
        count: Int
    ) {
        if (adapter != null && layoutManager is GridLayoutManager) {
            //val
            val animationParams =
                params.layoutAnimationParameters as GridLayoutAnimationController.AnimationParameters // CustomGridRecyclerView.kt:38 is the line where kotlin.TypeCastException occurs I have tried with GridLayoutAnimationController.AnimationParameters? as well but never succeeded 
            val columns = (layoutManager as GridLayoutManager?)!!.spanCount
            animationParams.count = count
            animationParams.index = index
            animationParams.columnsCount = columns
            animationParams.rowsCount = count / columns
            val invertedIndex = count - 1 - index
            animationParams.column = columns - 1 - invertedIndex % columns
            animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns
        } else {
            super.attachLayoutAnimationParameters(child, params, index, count)
        }
    }
}

它给我错误:

kotlin.TypeCastException: null cannot be cast to non-null type android.view.animation.GridLayoutAnimationController.AnimationParameters
        at pk.inlab.app.expenselogger.view.custom.CustomGridRecyclerView.attachLayoutAnimationParameters(CustomGridRecyclerView.kt:38)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3267)
        at android.view.View.draw(View.java:15507)
        at androidx.recyclerview.widget.RecyclerView.draw(RecyclerView.java:4219)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.draw(View.java:15507)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at androidx.coordinatorlayout.widget.CoordinatorLayout.drawChild(CoordinatorLayout.java:1246)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.draw(View.java:15507)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchDraw(PhoneWindow.java:2784)
        at android.view.View.draw(View.java:15507)
        at android.widget.FrameLayout.draw(FrameLayout.java:658)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2763)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:279)
        at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:285)
        at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:335)
        at android.view.ViewRootImpl.draw(ViewRootImpl.java:2939)
        at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2753)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2367)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1292)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6598)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:800)
        at android.view.Choreographer.doCallbacks(Choreographer.java:603)
        at android.view.Choreographer.doFrame(Choreographer.java:572)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:786)
        at android.os.Handler.handleCallback(Handler.java:815)
        at android.os.Handler.dispatchMessage(Handler.java:104)
        at android.os.L

我如何投射 android.view.animation.GridLayoutAnimationController.AnimationParameters 而不会出错?

更新

按照下面答案中的建议,我更改了我的代码

   val animationParams =
                params.layoutAnimationParameters as? GridLayoutAnimationController.AnimationParameters
            val columns = (layoutManager as GridLayoutManager?)!!.spanCount
            animationParams!!.count = count

得到这个:

kotlin.KotlinNullPointerException
        at pk.inlab.app.expenselogger.view.custom.CustomGridRecyclerView.attachLayoutAnimationParameters(CustomGridRecyclerView.kt:40)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3267)
        at android.view.View.draw(View.java:15507)
        at androidx.recyclerview.widget.RecyclerView.draw(RecyclerView.java:4219)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.draw(View.java:15507)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at androidx.coordinatorlayout.widget.CoordinatorLayout.drawChild(CoordinatorLayout.java:1246)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.draw(View.java:15507)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at android.view.View.updateDisplayListIfDirty(View.java:14376)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.View.draw(View.java:15204)
        at android.view.ViewGroup.drawChild(ViewGroup.java:3532)
        at android.view.ViewGroup.dispatchDraw(ViewGroup.java:3325)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchDraw(PhoneWindow.java:2784)
        at android.view.View.draw(View.java:15507)
        at android.widget.FrameLayout.draw(FrameLayout.java:658)
        at com.android.internal.policy.impl.PhoneWindow$DecorView.draw(PhoneWindow.java:2763)
        at android.view.View.updateDisplayListIfDirty(View.java:14384)
        at android.view.View.getDisplayList(View.java:14413)
        at android.view.ThreadedRenderer.updateViewTreeDisplayList(ThreadedRenderer.java:279)
        at android.view.ThreadedRenderer.updateRootDisplayList(ThreadedRenderer.java:285)
        at android.view.ThreadedRenderer.draw(ThreadedRenderer.java:335)
        at android.view.ViewRootImpl.draw(ViewRootImpl.java:2939)
        at android.view.ViewRootImpl.performDraw(ViewRootImpl.java:2753)
        at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2367)
        at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1292)
        at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6598)
        at android.view.Choreographer$CallbackRecord.run(Choreographer.java:800)
        at android.view.Choreographer.doCallbacks(Choreographer.java:603)
        at android.view.Choreographer.doFrame(Choreographer.java:572)
        at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:786)
        at android.os.Handler.handleCallback(Handler.java:815)
        at android.os.Handler.dispatchMessage(Handler.java:104)
        at android.os.Looper.loop(Looper.java:194)
        at android.app.ActivityThread.main(ActivityThread.java:5631)
        at java.lang

第一个问题是您正在尝试 cast 一个 nullable value (params.layoutAnimationParameters) to a non null type,当 params.layoutAnimationParametersnull 时,这会导致 null pointer exception

要解决此问题,请使用 kotlin 的 safe cast operator (as?),它会尝试 cast 给定 type 的值,如果不能,则只需 returns null.

第二个问题是 在你的 java 代码中如果 params.layoutAnimationParametersnull 那么你给它分配一个新的 GridLayoutAnimationController.AnimationParameters() ,

if (animationParams == null) {
   animationParams = new GridLayoutAnimationController.AnimationParameters();
   params.layoutAnimationParameters = animationParams;
}

我没有看到你在 kotlin 代码中做同样的事情。 我已经修改了你的 function 以使用safe cast 和适当的 null 检查,它应该工作。

override fun attachLayoutAnimationParameters(
        child: View?,
        params: ViewGroup.LayoutParams,
        index: Int,
        count: Int
) {
    if (adapter != null && layoutManager is GridLayoutManager) {
        var animationParams =
                params.layoutAnimationParameters as? GridLayoutAnimationController.AnimationParameters 
        if (animationParams == null) {
            animationParams = AnimationParameters()
            params.layoutAnimationParameters = animationParams
        }
        val columns = layoutManager.spanCount
        animationParams.count = count
        animationParams.index = index
        animationParams.columnsCount = columns
        animationParams.rowsCount = count / columns
        val invertedIndex = count - 1 - index
        animationParams.column = columns - 1 - invertedIndex % columns
        animationParams.row = animationParams.rowsCount - 1 - invertedIndex / columns
    } else {
        super.attachLayoutAnimationParameters(child, params, index, count)
    }
}

像这样使用它:

val animationParams =
                params.layoutAnimationParameters as? GridLayoutAnimationController.AnimationParameters

然后像这样将访问更改为空安全访问:

animationParams?.count = count