我正在尝试使用泛型参数创建一个抽象类,该类具有子类,应该调用方法而不必指定类型参数。 到目前为止我有这个:
abstract class AbstractClass<T : Any> @Autowired constructor(protected val delegate: MyService) {
inline fun <T: Any> myMethod(param: Any): T? {
return delegate.myMethod(param).`as`(T::class.java)
}
}
并实施:
class TesterWork @Autowired constructor(delegate: MyService) : AbstractClass<Tester>(delegate) {
}
现在,当调用myMethod
时,我必须指定类型参数:
testerWork.myMethod<Tester>("test")
我想知道是否可以自动推断出类型参数?
我可以以某种方式重写myMethod吗?请注意,我需要在方法中包含T::class.java
。
答案 0 :(得分:8)
你不能使用课程&#39;泛型参数作为一个通用参数(获取其T::class
标记),因为在运行时,通用参数被删除:Kotlin遵循Java's type erasure practice并且没有为类提供具体的泛型。
(Kotlin只有内联函数reified generics)
鉴于此,您可以通过并存储Class<T>
令牌,以便您可以使用它。
此外,您的示例中的myFunction
引入了一个通用参数,它将是一个新的泛型,不以任何方式连接到类泛型参数(命名T
只会增加混淆,请考虑它们T1
和T2
)。如果我做对了,你的意思就是班级&#39;通用而不是。
你可以做的是声明一个抽象val
,它会存储一个类令牌并重写该函数,以便它使用存储的类令牌:
abstract class AbstractClass<T : Any> constructor(protected val delegate: MyService) {
protected abstract val classToken: Class<T>
fun myMethod(param: Any): T? {
return delegate.myMethod(param).`as`(classToken)
}
}
然后,从AbstractClass
派生将需要覆盖classToken
:
class TesterWork constructor(delegate: MyService) : AbstractClass<Tester>(delegate) {
override val classToken = Tester::class.java
}
之后,您将能够在TesterWork
实例上调用该函数,而无需指定通用参数:
val service: MyService = ...
val t: Tester? = TesterWork(service).myMethod("test")
答案 1 :(得分:3)
这里有几个问题在起作用。首先,为了从泛型类型参数中检索类对象,您需要对其进行重新验证。为此,您需要声明<reified T: Any>
。
其次,您将声明泛型类型参数两次。一旦进入类声明abstract class AbstractClass<T : Any>
,一次进入方法声明inline fun <T: Any> myMethod
。那些T
不一样。从理论上讲,你可以省略方法签名中的那个,但是这不起作用,因为reification仅适用于不带类的内联方法。有关可能的解决方案,请参阅@ hotkey的答案。
答案 2 :(得分:2)
您可以为抽象类提供一个转换lambda:
abstract class AbstractClass<T : Any>(val delegate: MyService, val castFn: (Any) -> T) {
fun myMethod(param: Any): T? {
return castFn(delegate.myMethod(param))
}
}
class TesterWork(delegate: MyService) : AbstractClass<Tester>(delegate, {it as Tester}) {
}
你也可以让这个类非抽象,给它一个接口,让内联函数创建它:
interface Inter<T> {
fun myMethod(param: Any): T
}
class NotAbstractClass<T>(val delegate: MyService, val castFn: (Any) -> T) : Inter<T> {
override fun myMethod(param: Any): T {
return castFn(delegate.myMethod(param))
}
}
inline fun <reified T> makeClass(delegate: MyService): Inter<T> {
return NotAbstractClass<T>(delegate, { it as T })
}
然后您可以这样委托:
class TesterWork(delegate: MyService) : Inter<Tester> by makeClass(delegate)
val service: MyService = MyService()
val t: Tester? = TesterWork(service).myMethod("test")
答案 3 :(得分:1)
感谢Fabian Zeindly,我为几个扩展类编写了一个变体。在这种情况下,所有这些都应在抽象类的标题中声明。
abstract class AbstractAdapter<V: AbstractAdapter.ViewHolder, I: AbstractAdapter.Item> : RecyclerView.Adapter<V>() {
var list: List<I> = emptyList()
override fun getItemCount(): Int = list.size
open fun setItems(list: List<I>) {
this.list = list.toMutableList()
}
open class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView)
open class Item
}
然后使用新的内部类(ViewHolder,Item)扩展该类。
class NewAdapter(private var listener: View.OnClickListener?) : AbstractAdapter<NewAdapter.ViewHolder, NewAdapter.Item>() {
private var originalItems: List<Item> = emptyList()
override fun setItems(list: List<Item>) {
super.setItems(list)
this.originalItems = list.toMutableList()
this.list = list.toMutableList()
}
override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): ViewHolder {
val view = LayoutInflater.from(viewGroup.context)
.inflate(R.layout.layout, viewGroup, false)
return ViewHolder(view)
}
override fun onBindViewHolder(viewHolder: ViewHolder, i: Int) {
val item = list[i]
viewHolder.textView.text = item.text
viewHolder.textView.tag = item.id
}
class ViewHolder(itemView: View) : AbstractAdapter.ViewHolder(itemView) {
val textView: TextView = itemView.name
}
class Item(val id: Int, val text: String) : AbstractAdapter.Item()
}