Android 如何在不指定类型参数的情况下扩展kotlin泛型类以允许回收器视图的任何子类型?

Android 如何在不指定类型参数的情况下扩展kotlin泛型类以允许回收器视图的任何子类型?,android,generics,kotlin,android-recyclerview,Android,Generics,Kotlin,Android Recyclerview,因此,我在我的应用程序代码的各个地方都有以下类型的RecyclerView实现: package com.test; import android.content.Context; import android.view.View; import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import org.je

因此,我在我的应用程序代码的各个地方都有以下类型的
RecyclerView
实现:

package com.test;

import android.content.Context;
import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import org.jetbrains.annotations.NotNull;

import java.util.List;

public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.BaseViewHolder> {

    @NonNull
    private List<? extends ListItem> itemList;

    public ItemAdapter(@NotNull final List<? extends ListItem> itemList) {
        this.itemList = itemList;
    }

    @NonNull
    @Override
    public BaseViewHolder onCreateViewHolder(@NonNull final ViewGroup viewGroup, final int itemType) {
        final Context context = viewGroup.getContext();
        final BaseViewHolder BaseViewHolder;

        final View view = new View(context); // imagine an inflated view here

        switch (itemType) {
            case ListItem.TYPE_ONE:
                BaseViewHolder = new TypeOneViewHolder(view);
                break;

            case ListItem.TYPE_TWO:
                BaseViewHolder = new TypeTwoViewHolder(view);
                break;

            default: // try to get coupon view holder if item type valid
                throw new IllegalArgumentException("Invalid view type");
        }

        return BaseViewHolder;
    }

    @Override
    public void onBindViewHolder(@NonNull final BaseViewHolder baseViewHolder, final int position) {
        baseViewHolder.bind(itemList.get(position));
    }

    @Override
    public int getItemCount() {
        return itemList.size();
    }

    @Override
    public int getItemViewType(final int position) {
        return itemList.get(position).getItemType();
    }

    public interface ListItem {
        int TYPE_DEF = -1;
        int TYPE_ONE = 0;
        int TYPE_TWO = 1;

        int getItemType();
    }

    public static class ItemOne implements ListItem {
        @Override
        public int getItemType() {
            return TYPE_ONE;
        }
    }

    public static class ItemTwo implements ListItem {
        @Override
        public int getItemType() {
            return TYPE_TWO;
        }
    }

    public static abstract class BaseViewHolder<T extends ListItem> extends RecyclerView.ViewHolder {
        BaseViewHolder(@NonNull View itemView) {
            super(itemView);
        }

        public abstract void bind(final T item);
    }

    public static class TypeOneViewHolder extends BaseViewHolder<ItemOne> {
        TypeOneViewHolder(@NonNull View itemView) {
            super(itemView);
        }

        @Override
        public void bind(ItemOne item) {
            // do something...
        }
    }

    public static class TypeTwoViewHolder extends BaseViewHolder<ItemTwo> {
        TypeTwoViewHolder(@NonNull View itemView) {
            super(itemView);
        }

        @Override
        public void bind(ItemTwo item) {
            // do something...
        }
    }
}

package.com.test;
导入android.content.Context;
导入android.view.view;
导入android.view.ViewGroup;
导入androidx.annotation.NonNull;
导入androidx.recyclerview.widget.recyclerview;
导入org.jetbrains.annotations.NotNull;
导入java.util.List;
公共类ItemAdapter扩展了RecyclerView.Adapter{
@非空

private List将
扩展列表项
T将列表项
扩展为仅
列表项
对您有效吗?这在kotlin中应该有效

可以在此处找到其中一个示例:


否则,您可以使用与java中的对象相同的类型
Any
。它将使适配器通用,即使这可能不是您真正想要的。

扩展ListItem
T扩展ListItem
更改为仅
ListItem
,这在kotli中应该有效n

可以在此处找到其中一个示例:


否则,您可以使用类型
Any
,它与java中的
Object
相同。它将使适配器通用,即使这可能不是您真正想要的。

我相信您可以使用BaseViewHolder键入参数:


公共类ItemAdapter:RecyclerView.Adapter

在BaseViewHolder中,只需键入泛型参数T,然后将其强制转换为bind()方法。类似的代码适用于我的情况:


公共抽象类BaseViewHolder(视图:视图):RecyclerView.ViewHolder(视图){}
我相信您可以使用BaseViewHolder键入参数:


公共类ItemAdapter:RecyclerView.Adapter

在BaseViewHolder中,只需键入泛型参数T,然后将其强制转换为bind()方法。类似的代码适用于我的情况:


公共抽象类BaseViewHolder(view:view):RecyclerView.ViewHolder(view){}
在Java中使用它的方式是在ItemAdapter中使用原始类型声明BaseListItem。因此它没有以安全的方式使用泛型。当调用
BaseViewHolder.bind(itemList.get(position))时
,您正在隐式地将该列表位置的任何项强制转换为列表中该位置的视图持有者的类型。Kotlin禁止原始类型,因为它违背了使用泛型的目的。Java只允许原始类型,因为向后兼容

您的Java代码可以工作,因为您的列表项可能正在报告其正确的项类型,这些项类型在
onCreateViewHolder
中使用。因此,只有在您在其中出错的情况下,转换才会导致问题

通过从BaseListItem类中删除泛型类型,并使
bind
函数如下所示,可以在Kotlin(或Java)中获得等效的功能:

abstract fun bind(item: ListItem)

class TypeOneViewHolder: BaseViewHolder() {
    fun bind(item: ListItem){
        val itemOne = item as ItemOne
        // use itemOne for binding
    }
}
如果您不喜欢“不安全施法”警告,可以进行安全施法:

fun bind(item: ListItem){
    (item as? ItemOne)?.let { itemOne ->
        // use itemOne for binding
    } ?: error("Item type mismatch. ${this::class} with ${item::class}")
}
或者,如果您想继续使用您的模式,让RecyclerView.Adapter在引擎盖下代表您进行不安全的强制转换,我认为您可以使用星形投影进行设置。由于您自己的代码从未在BaseViewHolder实现中调用过任何方法,Kotlin不应该有任何抱怨。我没有尝试是我自己,但也许会有用:

abstract class BaseViewHolder<in T: ListItem>(itemView: ItemView): ViewHolder(itemView) {
    abstract fun bind(item: T)
}

class ItemAdapter(val itemList: List<out ListItem>): Adapter<BaseViewHolder<*>>(){

    //...

}
抽象类BaseViewHolder(itemView:itemView):ViewHolder(itemView){
抽象趣味绑定(项目:T)
}
类ItemAdapter(val itemList:List):适配器(){
//...
}

在Java中使用它的方式是在ItemAdapter中使用原始类型声明BaseListItem。因此它没有以安全的方式使用泛型。当调用
baseViewHolder.bind(itemList.get(position))时
,您正在隐式地将该列表位置的任何项强制转换为列表中该位置的视图持有者的类型。Kotlin禁止原始类型,因为它违背了使用泛型的目的。Java只允许原始类型,因为向后兼容

您的Java代码可以工作,因为您的列表项可能正在报告其正确的项类型,这些项类型在
onCreateViewHolder
中使用。因此,只有在您在其中出错的情况下,转换才会导致问题

通过从BaseListItem类中删除泛型类型,并使
bind
函数如下所示,可以在Kotlin(或Java)中获得等效的功能:

abstract fun bind(item: ListItem)

class TypeOneViewHolder: BaseViewHolder() {
    fun bind(item: ListItem){
        val itemOne = item as ItemOne
        // use itemOne for binding
    }
}
如果您不喜欢“不安全施法”警告,可以进行安全施法:

fun bind(item: ListItem){
    (item as? ItemOne)?.let { itemOne ->
        // use itemOne for binding
    } ?: error("Item type mismatch. ${this::class} with ${item::class}")
}
或者,如果您想继续使用您的模式,让RecyclerView.Adapter在引擎盖下代表您进行不安全的强制转换,我认为您可以使用星形投影进行设置。由于您自己的代码从未在BaseViewHolder实现中调用过任何方法,Kotlin不应该有任何抱怨。我没有尝试是我自己,但也许会有用:

abstract class BaseViewHolder<in T: ListItem>(itemView: ItemView): ViewHolder(itemView) {
    abstract fun bind(item: T)
}

class ItemAdapter(val itemList: List<out ListItem>): Adapter<BaseViewHolder<*>>(){

    //...

}
抽象类BaseViewHolder(itemView:itemView):ViewHolder(itemView){
抽象趣味绑定(项目:T)
}
类ItemAdapter(val itemList:List):适配器(){
//...
}

Maks如果我指定了
BaseViewHolder
,我就不能使用泛型参数t。我目前正在使用类型转换,但我想要一个泛型解决方案,在java版本中,我不必显式地键入转换。在我看来,这是一个泛型解决方案,正如您在扩展类时声明泛型类型一样。我仍然建议创建d类BaseViewHolder或BaseViewHolder的eclaration。在此抽象类内使用时,您将有权访问ListItem参数/函数。BaseViewHolder与泛型BaseViewHolder不起作用,请尝试将其输出公共类ItemAdapter:RecyclerView.Adapter()复制自我的代码,其中我的ListItem是一个密封的类。编译器说它对接口也很好,但我将尝试运行它,看看它是否可以在以后运行