#android #kotlin #generics
#Android #kotlin #обобщения
Вопрос:
Я так запутался, я искал, как работают дженерики kotlin, но я совсем не понимаю.
В принципе, у меня есть BaseAnimalDetail
класс, который используется с некоторыми классами, расширяющими Animal
класс. На данный момент when
в BaseAnimalDetail
классе их много, поэтому моя цель — избежать всего этого when
, просто расширив BaseAnimalDetail whenever I need, for each class that extends
Animal `
open class BaseAnimalDetail<T : Animal?> : LinearLayout{
private var animal: T? = null
fun setAnimal(animal:T){
this.animal = animal
}
private fun play() {
when(animal){
is Horse -> playWithHorse...
is Dog -> playWithDog...
is Cat -> playWithCat...
}
animal?.let { it.play() } //this is not working but animal?.play works fine, WHY?
}
...More code
}
Как вы можете видеть, when
это очень некрасиво, поэтому я хотел бы сделать BaseAnimalDetail
абстрактный и реализовать его, как я уже сказал.
Приведенный выше код — всего лишь пример, но он поможет мне понять, как работают дженерики в Kotlin. Для меня обязательно иметь переменную T в базовом классе
Кроме animal?.let { it.play() }
того, ПОЧЕМУ не компилируется?
Комментарии:
1. Можете ли вы опубликовать свой класс Animal?
Ответ №1:
В случае, который вы описываете, вы хотите, чтобы у каждого типа Animal был свой собственный подкласс BaseAnimalDetail, соответствующий ему. Итак, вы должны сделать BaseAnimalDetail абстрактным классом, а затем вы можете создавать подклассы для каждого животного.
Кстати, поскольку у Kotlin есть свойства, излишне делать animal
private и иметь функцию setter .
abstract class BaseAnimalDetail<T : Animal> {
var animal: T? = null
abstract fun play()
}
class CatDetail: BaseAnimalDetail<Cat>(){
override fun play() {
println("toss yarn to $animal")
}
}
Но если все эти разные функции различны для каждого типа Animal, мне кажется, что у вас вообще не должно быть отдельного класса BaseAnimalDetail, и его функциональность должна быть только в различных классах Animal. В качестве альтернативы, если есть определенные модели поведения, которые разделяют некоторые животные, вы можете создавать различные классы поведения, которые можно добавлять в класс Animal (стратегия разработки композиции).
Комментарии:
1. Я не понимаю, зачем вы создали этот конструктор. У меня есть метод setAnimal, чтобы установить его, когда мне нужно. И для меня обязательно иметь переменную animal T, чтобы использовать ее из BaseAnimalDetail, когда это необходимо
2. Свойство не обязательно должно быть в конструкторе (это просто для удобства), но
setAnimal
оно полностью избыточно в Kotlin, потомуanimal
что является свойством. Он имеет подразумеваемый получатель и установщик с вспомогательным полем. Вы можете настроить установщик на сайте объявления позже, не изменяя никаких подписей, точно так же, как с установщиком Java.3. Я обновил его, чтобы извлечь свойство из конструктора. Однако это не имеет отношения к тому, что вы спрашивали, не так ли?
4. Мне только что пришло в голову, что, возможно, вы не знали, что свойства в конструкторе так же действительны, как и свойства вне конструктора. Они не являются частными для конструктора, но доступны для использования в течение всего срока службы объекта, как и другие свойства. Помещение их в конструктор позволяет удобно устанавливать их при создании экземпляра класса.
Ответ №2:
Не уверен, правильно ли вы понимаете, но то, что вы могли бы сделать, это то, что вы сказали — преобразуйте свой BaseAnimalDetail
абстрактный класс в абстрактный класс с помощью abstract fun fun play()
и разрешите Animal
class расширять BaseAnimalDetail
или разрешать ur Horse
, Dog
расширять его напрямую.
abstract class BaseAnimalDetail {
abstract fun play()
}
open class Animal : BaseAnimalDetail() {
override fun play() = Unit
}
class Horse : Animal() {
override fun play() {
playWithHorse()
}
private fun playWithHorse() {}
}
class Dog : BaseAnimalDetail() {
override fun play() {
playWithDog()
}
private fun playWithDog() {}
}
Также интерфейс может предоставлять «всякий раз, когда мне нужно», поскольку наша цель — предоставить функцию, и в ней нет более глубокой логики.
Комментарии:
1. Для меня обязательно иметь переменную animal T, чтобы использовать ее из BaseAnimalDetail, когда это необходимо, кроме того, вы знаете, почему animal? .пусть { it.play() } не компилируется? Спасибо
2. Для чего ошибка
animal?.let { it.play() }
? Похоже, он должен работать нормально, хотя это просто подробный способ написанияanimal?.play()
.3. Ошибка вывода типа. Ожидаемое несоответствие типов: требуется: найдено животное: T!
4. Я думаю, вы удалили слишком много контекста того, что происходит вокруг, чтобы мы могли увидеть проблему. То, что у вас есть выше, отлично работает как есть: pl.kotl.in/hvAe2OnNy
5. Эта ошибка может возникнуть, если
play
функция имеет возвращаемый тип.
Ответ №3:
Не уверен, зачем вам для этого нужны дженерики, вот моя реализация
abstract class Animal {
abstract fun play()
}
class Horse : Animal() {
override fun play() {
playWithHorse()
}
private fun playWithHorse() {}
}
class Dog : Animal() {
override fun play() {
playWithDog()
}
private fun playWithDog() {}
}
open class BaseAnimalDetail(var animal: Animal?=null) {
private fun play() {
animal?.play()
}
}
class HorseDetail(var horse: Horse? = null): BaseAnimalDetail(horse){
}
Вы можете удалить play
функцию из BaseAnimalDetail, вместо этого вызывая ее напрямую horse?.play()
.
Комментарии:
1. Мой код — всего лишь пример чего-то более сложного, что мне нужно, мне нужно иметь переменную в BaseAnimalDetail (не в конструкторе). Спасибо
2. Это аргумент по умолчанию, поэтому вам не нужно передавать значение в конструкторе.
3. Обобщения необходимы, если вы хотите получить
animal
и автоматически обработать его компилятором как известный подтип.