Параметр типа reated Kotlin не может использоваться как параметр типа в теле функции

Параметр типа reified type в Kotlin предотвращает стирание параметра типа и позволяет определить тип параметра во время выполнения. Это позволяет компилировать и запускать следующий код, как ожидалось:

inline fun <reified T> isA(value: Any) = value is T 

Однако, когда я пытаюсь использовать «T» в качестве параметра типа вместо автономного, я получаю сообщение о том, что это стираемый тип. Об этом свидетельствует следующий код, который предназначен только для иллюстративных целей :

 inline fun <reified T> isListOfA(name: String): Boolean { val candidate = Class.forName(name) return candidate is List<T> } 

Это связано с техническим ограничением? Если да, то каково это ограничение?

Техническое ограничение, которое мешает вам выполнять это, – стирание стилей типа generics на JVM . В принципе, во время выполнения объект общего типа List<T> становится просто List который работает с объектами: только во время компиляции проверяется безопасность типа для назначений и вызовов функций. Фактический параметр типа T существует только во время компиляции, а затем стирается. Он не может быть восстановлен во время выполнения (по крайней мере, на данный момент: есть проект Valhalla, который может вводить генерируемые генерируемые среды для JVM в один день).

В не-встроенной функции Kotlin (и с неинициализированным параметром типа) вы даже не можете выполнить первый вид проверки, value is T , потому что обычный тип параметра также будет удален.

С параметрами типа reified тело функции встраивается в свои сайты вызова с фактическим (или inferred) параметром типа, замененным на T : когда вы вызываете isA<String>("abc") , сайт вызова будет иметь байт-код с instanceof check для String .

Но даже с параметрами типа reified вы не можете интроспектировать общие типы: вы можете проверить, something is List<*> что- something is List<*> но не что- something is List<String> : аргумент типа не хранится нигде во время выполнения.

Также обратите внимание, что isA<List<String>>(listOf(1, 2, 3)) вернет true . Вот как этот нечетный случай обрабатывается в Котлине: только некоторая общая часть этого типа может быть проверена во время выполнения, и так оно и есть.

в Kotlin нет способа сделать это, так как Java стирает параметр типа T общего типа в Object / upper-bounded type во время компиляции.

Первый подход может работать, потому что value is T включено в функцию call-site с типом reified, например:

 //val is_string = isA<String>(1) // inline into the call-site function as below: val i:Int = 1 // v--- the actual type argument is inlined here val is_string = 1 is String 

Параметрированные типы всегда стираются во время выполнения. Таким образом, вы можете проверить, что значение является экземпляром T но не экземпляром T<V> , независимо от того, являются ли T и V подтверждены или жестко закодированы.

Однако, даже если это было возможно, ваш примерный код не имеет смысла, поскольку он проверяет, является ли тип с этим именем экземпляром List, а не проверяет, является ли тип с этим именем ожидаемым типом List.

Если у вас есть экземпляр объекта и вы хотите проверить, что это список, содержащий только элементы ожидаемого типа, вы все равно можете написать что-то вроде этого:

 inline fun <reified T> isListOfA(instance: Any) = instance is List<*> && instance.all { it is T } 

Очевидно, я не сформулировал свой вопрос надлежащим образом, чтобы получить ответ на форму, которую я хотел. Большинство ответов здесь – некоторые вариации «потому что вы не можете сделать это на Java». Ну, вы не можете делать x instanceof T в Java, но вы можете сделать x is T в Kotlin. Я ищу базовый практический roadblock, а не правило Java. В конце концов, правила нарушаются.

Из моего комментария к первому ответу здесь, переформулированный вопрос: если objectref is T можно objectref is T работать в Котлине каким-то механизмом X почему невозможно objectref is SomeClass<T> для работы этим же механизмом?

tl; dr answer: потому что во время выполнения не будет объекта Class для SomeClass<T> .

Более длинный ответ: сначала мы должны понимать механизм X , который должен генерировать instanceof инструкции байт-кода для is T Эта инструкция принимает objectref и имя N некоторого класса C , где N определяется из контекста компилятором. Во время выполнения класс C полученный из N будет использоваться для оценки выражения objectref is T Для того чтобы эта оценка возникла, объект класса для C должен быть инстанцирован. Таким образом, для использования этого же механизма для objectref is SomeClass<T> тогда N будет SomeClass<T> . Из-за стирания типа не будет объекта класса для SomeClass<T> поэтому невозможно создать необходимую команду instanceof и тем самым применить тот же механизм. Кроме того, команда instanceof не может принимать имя формы SomeClass<T> . Поэтому, если objectref is SomeClass<T> он должен быть найден и реализован в Kotlin. Такой механизм может существовать или не существовать.

Я знаю, что некоторые могут сказать, что это то же самое, что и некоторые другие ответы. Однако, к лучшему или худшему, мой стиль обучения заключается в том, чтобы понять, как все работает на металле, а затем синтезировать его против абстрактной модели. В этом случае понятие стирания Java Generics является абстрактной моделью (или ее частью). Действительно, «стирание» кажется мне неуклюжим, если я не пойму хотя бы один способ, который он реализует в рабочей реализации.