关于父类与子类的Kotlin泛型问题及Java RecyclerView适配器转Kotlin需求
嘿,我来帮你搞定这两个问题——先聊聊Kotlin里父类子类的泛型使用,再把你的Java RecyclerView适配器转成Kotlin代码,顺便讲清楚转换时的关键细节~
一、Kotlin中父类与子类的泛型使用要点
咱们先把核心规则理清楚,避免踩坑:
- 协变与逆变(核心!):Kotlin用
out和in解决泛型的类型兼容问题,比Java的通配符更清晰。比如父类定义open class Base<out T>,就表示T只能被读取(协变,对应Java的? extends T),这时Base<String>可以直接赋值给Base<Any>;如果是open class Base<in T>,表示T只能被写入(逆变,对应Java的? super T),Base<Any>可以赋值给Base<String>。 - 泛型约束:和Java的
extends对应,Kotlin用:指定上界,比如class MyAdapter<T : Element>,意思是T必须是Element的子类/实现类。如果需要多个约束,用where关键字:class MyAdapter<T> where T : Element, T : Serializable。 - 继承时的泛型传递:如果父类是泛型类,子类有两种选择:要么直接指定具体类型,比如
class MovieAdapter : RecyclerAdapter<Movie>;要么继续保留泛型参数,比如class GenericAdapter<T : Element> : RecyclerAdapter<T>,这样子类也能复用泛型能力。 - 星投影:当你不确定泛型具体类型时,用
*代替,比如RecyclerAdapter<*>,相当于Java的RecyclerAdapter<?>,但Kotlin会根据协变逆变自动限制读写权限——比如如果父类是out协变,RecyclerAdapter<*>只能读不能写,更安全。
二、Java RecyclerView适配器转Kotlin代码
先把你给出的Java代码补全(原代码截断了),再转换成Kotlin版本,同时优化写法:
转换后的Kotlin代码
package com.mkhaleghy.cinema.adapter import android.content.Context import android.support.v7.widget.RecyclerView import android.util.SparseArray import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import com.mkhaleghy.cinema.R import com.mkhaleghy.cinema.daylist.Movie import java.util.ArrayList import java.util.HashMap // 泛型约束和Java完全对应:T必须是Element的子类 class RecyclerAdapter<T : Element> : RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() { // Kotlin要求变量初始化,可空类型用?标记,非空直接初始化 private var context: Context? = null private var items: MutableList<T> = ArrayList() private var headers: SparseArray<HashMap<String, Any>> = SparseArray() // 对应Java的构造方法,也可以改成更简洁的主构造(后面会说) constructor(context: Context?) { this.context = context } constructor(context: Context?, items: MutableList<T>) { this.context = context this.items = items } // 内部ViewHolder类,用inner关键字才能访问外部类成员(如果需要的话) inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { // 这里可以添加你的控件引用,比如: // val movieName: TextView = itemView.findViewById(R.id.tv_movie_name) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { // 空安全处理:context为null时LayoutInflater.from会抛异常,和Java一致 val inflater = LayoutInflater.from(context) // 替换成你的实际item布局ID val itemView = inflater.inflate(R.layout.item_movie, parent, false) return ViewHolder(itemView) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val currentItem = items[position] // 绑定数据,比如如果是Movie类型可以强转: // if (currentItem is Movie) { // holder.movieName.text = currentItem.title // } } override fun getItemCount(): Int = items.size // Kotlin单行函数写法 // 新增常用工具方法,方便添加数据 fun addItem(item: T) { items.add(item) notifyItemInserted(items.size - 1) } fun addItems(newItems: List<T>) { val startPos = items.size items.addAll(newItems) notifyItemRangeInserted(startPos, newItems.size) } } // 假设你的Element基类是这样的,确保项目中存在这个类 open class Element
转换时的关键优化点
- 构造方法简化:上面的次构造可以改成更简洁的主构造,代码更紧凑:
class RecyclerAdapter<T : Element>( private var context: Context?, private var items: MutableList<T> = ArrayList() ) : RecyclerView.Adapter<RecyclerAdapter.ViewHolder>() { // 这里不需要再写构造方法,直接用主构造的参数 private var headers: SparseArray<HashMap<String, Any>> = SparseArray() // 其他代码不变... } - 空安全:Kotlin强制空安全,
context被标记为Context?,调用时如果需要避免空指针,可以用context?.let { inflater -> ... }做安全调用。 - 单行函数:
getItemCount()直接写成override fun getItemCount(): Int = items.size,比Java的写法更简洁。 - 类型判断:在
onBindViewHolder中,用is关键字做类型检查,比Java的强转更安全,不会抛出ClassCastException(如果类型不匹配,代码不会执行强转后的逻辑)。
内容的提问来源于stack exchange,提问作者max
相关产品推荐
相关产品推荐

