Как безопасно составить отраженный класс в Котлине - PullRequest
0 голосов
/ 23 февраля 2019

Мне нужно динамически загружать классы во время выполнения в Kotlin.Я хотел бы проверить, что они реализуют мой интерфейс, и если так, все зеленые.К сожалению, «умные броски» Котлина меня подводят:

var className = "some.class.Name"
val unsafeClass = Class.forName(className).kotlin
require(unsafeClass.isSubclassOf(MyInterface::class)) {
    "Class '$className' is not a MyInterface"
}
val safeClass = unsafeClass as KClass<MyInterface>
                            ^^^^^^^^^^^^^^^^^^^^^^
                            Unchecked cast: KClass<out Any!> to KClass<MyInterface>

Я четко проверяю, реализует ли класс данный интерфейс.Могу ли я перефразировать этот код, чтобы избежать предупреждения?

Я пытался проверить с is KClass<MyInterface>, но я получаю ошибку стирания типа (очевидно, потому что информация общего типа исчезает во время выполнения.)


Редактировать: чтобы уточнить, мое приложение должно читать имена классов "some.class.Name" при запуске, во время настройки;загрузить эти классы;убедитесь, что они удовлетворяют интерфейсу;и сохраните ссылку Class или KClass для дальнейшего использования.Во время выполнения он будет использовать эти ссылки для создания объектов, используя cls.createInstance() или что-то подобное.

Мой вопрос: есть ли способ сделать это без получения небезопасных предупреждений о приведении?

Я могу либополучить предупреждение во время конфигурации, когда я приведу KClass<*> к KClass<MyInterface> (даже если я require d, чтобы класс был подклассом), но потом я не получаю предупреждения, потому что .createInstance() на KClass<MyInterface> ссылка на класс возвращает проверенный тип MyInterface экземпляр.

Или я могу сохранить ссылки как KClass<*>, без предупреждений во время конфигурации, но тогда я получу предупреждение в том месте, где ясоздать экземпляры, потому что мне нужно небезопасно привести Object экземпляров к MyInterface.

Есть ли какое-либо решение, которое удовлетворит компилятор?

Ответы [ 2 ]

0 голосов
/ 23 февраля 2019

Это приведение не только не проверено, но на самом деле неверно: поскольку AMyInterfaceImpl::class имеет тип KClass<AMyInterfaceImpl> и KClass не является ковариантным (по уважительной причине), оно не имеет тип KClass<MyInterface>.Вы можете видеть, что из этого кода не компилируется:

class AMyInterfaceImpl : MyInterface { ... }

val cls: KClass<MyInterface> = AMyInterfaceImpl::class

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

KClass<out MyInterface> было бы правильно, но я не думаю, что компилятор поймет это и разрешит умное приведение.Просто слишком редко учить компилятору.

0 голосов
/ 23 февраля 2019

JVM и Kotlin реализуют дженерики только на уровне компилятора.Нельзя увидеть универсальные параметры универсального класса во время выполнения.https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

Во время выполнения нет разницы между Class<*> и Class<MyInterface>.Это два экземпляра типа Class.

Предупреждение, которое у вас есть, означает, что у вас нет информации в универсальном параметре во время выполнения, компилятор тоже не может его проверить, и он может только доверять вам

Я не вижу причины, приведеннойот KClass до KClass<MyInterface>.Это необходимо только для объекта, а не для его класса.Кроме того, возможно, вместо этого можно использовать Class<*>, например:

val className = "some.class.Name"
val unsafeClass = Class.forName(className)
require(MyInterface::class.java.isAssignableFrom(unsafeClass)) {
    "Class '$className' is not a MyInterface"
}
val safe = unsafeClass.newInstance() as MyInterface
...