Как правильно создать новый экземпляр универсального класса в kotlin?
Я использую следующую инициализацию:
val entityClass = javaClass<Class<T>>()
var entity = entityClass.newInstance().newInstance()
но это неправильно и вызывает IllegalAccessException
на java.lang.Class.newInstance(Class.java:1208)
1 ответ
Если вы позволите IntelliJ добавить явную информацию о типе, вы увидите, что entityClass
на самом деле типа Class<Class<String>>
, Я не уверен, что ты этого хочешь. В строке 2 вы сначала создаете экземпляр Class<T>
а затем один из T
но это все равно невозможно, потому что общая информация о T
теряется во время выполнения. Кроме того, вы не можете создавать экземпляры объектов класса напрямую.
Решение
Одним из возможных решений было бы добавить параметр типа Class<T>
к вашей функции или классу и использовать его для создания таких объектов, как это.
fun <T> foo(entityClass: Class<T>) {
var entity: T = entityClass.newInstance()
}
fun test() {
foo(Object::class.java)
}
Но на самом деле есть более элегантное решение без использования отражения. Определить параметр типа метода () -> T
и использовать ссылки на конструктор. Вот мой связанный вопрос о ссылках на конструктор и вот код:
fun <T> foo2(factory: () -> T) {
var entity: T = factory()
}
fun test() {
foo2(::Object)
}
Благодаря Kirill Rakhman я написал аналогичный ответ (для Android-адаптера). Разница здесь в параметре класса.
private fun <T> createItem(
viewGroup: ViewGroup,
layoutRes: Int,
method: (View) -> T
): T {
val view = LayoutInflater.from(viewGroup.context).inflate(layoutRes, viewGroup, false)
return method(view) // Creates T(view).
}
Тогда используйте это так:
override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): AbstractViewHolder {
return when (viewType) {
LOADER -> createItem(viewGroup, R.layout.row_loader, ::LoaderViewHolder)
DATE -> createItem(viewGroup, R.layout.row_date, ::DateViewHolder)
else -> throw IllegalStateException("Wrong class")
}
}
Вот LoaderViewHolder
а также DateViewHolder
являются потомками AbstractViewHolder
.