Абстрактный класс Котлина с общим параметром и методами, которые используют тип param

Я пытаюсь создать абстрактный класс с общим параметром, который будет иметь подклассы, которые должны вызывать методы без указания параметров типа . У меня это до сих пор:

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 внутри метода.

Вы не можете использовать универсальный параметр класса в качестве обобщенного типа (получение его токена T::class ), поскольку во время выполнения генерируемый параметр стирается: Kotlin следует практике стирания типа Java и не имеет повторных дженериков классов.
(Котлин подтвердил обобщения только для встроенных функций)

Учитывая это, вам решать передать и сохранить токен Class<T> чтобы вы могли его использовать.

Кроме того, myFunction в вашем примере вводит общий параметр, и он будет новым родовым, не связанным с универсальным параметром класса любым способом (именование обоих T только добавляет путаницу, рассматривает их T1 и T2 ). Если я правильно понял, вы имели в виду вместо этого общий класс.

Вероятно, что вы можете сделать, это объявить абстрактный 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") 

Здесь есть пара проблем. Во-первых, для того, чтобы получить объект класса из параметра типового типа, вам необходимо его восстановить. Для этого вам нужно объявить <reified T: Any> .

Во-вторых, вы дважды объявляете параметр типового типа. Однажды в объявлении класса abstract class AbstractClass<T : Any> и один раз в объявлении метода inline fun <T: Any> myMethod . Те T s не совпадают. Теоретически вы можете просто исключить из подписи метода, но это не работает, потому что reification работает только с встроенными методами, а не с классами. Для возможного решения этого вопроса обратитесь к ответу @ hotkey.

Вы можете дать абстрактному классу кастинг лямбда:

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