You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于父类与子类的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 08:02:07