An abstract Kotlin class with a common parameter and methods that use the param type

I am trying to create an abstract class with a common parameter that will have subclasses that should call methods without specifying type parameters . I still have this:

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) } } 

And implementation:

 class TesterWork @Autowired constructor(delegate: MyService) : AbstractClass<Tester>(delegate) { } 

Now when calling myMethod I need to specify an argument of the type:

 testerWork.myMethod<Tester>("test") 

I was wondering if it is possible to infer a type argument automatically? Can I recycle myMethod somehow? Note that I need to have T::class.java inside the method.

+7
source share
4 answers

You cannot use the universal parameter of a class as a generalized generalized (getting its T::class token), because at run time the universal parameter is erased: Kotlin follows the practice of erasing Java types and does not have generalized generic types for classes.
(Kotlin has improved generics for built-in functions only)

Given this, you need to transfer and save the Class<T> token so you can use it.

In addition, myFunction in your example introduces a universal parameter, and it will be a new universal parameter that has nothing to do with the universal parameter of the class (naming both T only adds confusion, consider them as T1 and T2 ). If I understood correctly, you meant the class 'generic'.

You can probably declare an abstract val that will store the class token, and rewrite the function so that it uses the stored class token:

 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) } } 

Then, a derivative of AbstractClass will require an override of classToken :

 class TesterWork constructor(delegate: MyService) : AbstractClass<Tester>(delegate) { override val classToken = Tester::class.java } 

After that, you can call the function on the TesterWork instance without specifying a universal parameter:

 val service: MyService = ... val t: Tester? = TesterWork(service).myMethod("test") 
+8
source

There are a couple of issues here. First, in order to get a class object from a type parameter, you need to restore it. To do this, you need to declare <reified T: Any> .

Secondly, you declare a type parameter twice. Once in the declaration of the abstract class AbstractClass<T : Any> and once in the declaration of the inline fun <T: Any> myMethod . Those T do not match. Theoretically, you can simply exclude a method from the signature, but this does not work because reification only works with built-in methods, not classes. For a possible solution to this question, refer to @hotkey's answer.

+3
source

You can give the abstract class cast 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}) { } 

Perhaps you can also make the class non-abstract, give it an interface and let the built-in function create it:

 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 }) } 

Then you can delegate like this:

 class TesterWork(delegate: MyService) : Inter<Tester> by makeClass(delegate) val service: MyService = MyService() val t: Tester? = TesterWork(service).myMethod("test") 
+2
source

Thanks to Fabian Zeyndli I wrote an option for several extended classes. In this case, they should all be declared in the name of the abstract class.

 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 } 

Then extend the class with new inner classes (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() } 
+1
source

Source: https://habr.com/ru/post/1245089/


All Articles