Если вы подумаете о том, что на самом деле означают модификаторы in
и out
, вы скоро поймете, что in OR out
на самом деле не имеет смысла.
Давайте возьмемсначала посмотрите на out
.Наличие <out T>
не означает, что «Эта функция принимает T
и ее подкласс в качестве параметра».На самом деле это означает «Эта функция не будет делать ничего, что не может сделать параметр класса T
или любой из его подклассов». Этот модификатор накладывает ограничения на то, что вы можете сделать с этим параметром.
Проверьте этот код:
fun doThingsWithBag(bag: Bag<out Dog>) {
bag.getAny().walk() // allowed
bag.put(Dog()) // not allowed
}
Вы не можете класть в эту сумку ни одной собаки,потому что эта сумка на самом деле может быть типа Bag<Pug>
, а Bag<Pug>
не будет принимать случайные Dog
.
То же самое относится и к in
.<in T>
не означает, что «Эта функция принимает T
и ее супер».Это больше похоже на «Эта функция не будет делать ничего T
или любой из ее суперклассов не может делать».
fun doThingsWithBag(bag: Bag<in Dog>) {
bag.put(Dog()) // allowed
bag.getAny().walk() // not allowed
}
Теперь вы можете положить собаку в сумку, но вы ничего не можете поделать с собаками внутри сумки, потому что сумка на самом деле может быть Bag<Animal>
, и нет никакой гарантии, что«вещь», которую вы достаете из сумки, - это Dog
.
Итак, как вы можете видеть, существует четкое различие между тем, что могут делать in
и out
, и поэтому они не могут сосуществовать.
Даже интерфейс, предложенный в другом ответе, не сильно вам поможет, потому что вашему классу Animal
также потребуется реализовать тот же интерфейс, что означает, что Cat
такжеиметь доступ к этому интерфейсу.Единственный способ обойти это - заставить work
метод принять Bag<Any>
и вручную проверить его универсальный класс.
Обновление:
Что вы пытаетесьсделать невозможно в ООП.То, что вы ищете, в основном выглядит примерно так:
update(Animal()) // allowed
update(Dog()) // allowed
update(Pug()) // allowed
update(Cat()) // not allowed (compile time error)
Однако компилятор не может помешать вам сделать это:
val animal: Animal = Cat()
update(animal) // ???
Вы можете сделать что-то подобноетолько во время выполнения, проверяя тип ввода вручную.