Почему Java не принимает возвращаемый тип, который подразумевается дженериками?

#java #generics

#java #дженерики

Вопрос:

 public class Role<A extends Actor> {
    protected A actor;
    protected A constructActor() {
        return new Actor();
    }
}
 

Почему этот Java-код не компилируется? Моя среда разработки сообщает мне, что constructActor() должен возвращать тип A, но не тип субъекта. Но, насколько я понимаю, я ясно дал понять, что A будет субъектом или расширением субъекта. Итак, почему Java ведет себя таким образом?

Редактировать:

Вы могли бы помочь мне в дальнейшем с моей идеей «обобщенного» фабричного шаблона:

Метод constructActor() на самом деле не был предназначен для того, чтобы всегда возвращать экземпляр Actor (следовательно, защищенный доступ), но был своего рода вариантом по умолчанию для каждого дочернего класса, который не должен создавать указанную версию Actor:

 // the first two cases should construct specified versions of the Actor
public class HeroRole<HeroActor> {
    @Override protected HeroActor constructActor() {
        return new HeroActor();
    }
}
public class EnemyRole<EnemyActor> {
    @Override protected EnemyActor constructActor() {
        return new EnemyActor();
    }
}

// the following classes should construct a regular Actor by default:
public class AnimalRole<Actor> {
}
public class GhostRole<Actor> {
}
 

Причина использования дженериков заключается в том, что каждый дочерний класс Роли знает своего Актера настолько точно, насколько это возможно, чтобы предотвратить приведения. Как вы можете видеть, существуют дочерние классы ролей, которые должны создавать определенные версии класса Actor (Герой, Враг). Но для других классов (Animal, Ghost) Я думал, что смогу сохранить реализации метода constructActor(), следуя моей первоначальной идее. Это не сработало. Есть ли какой-либо другой способ определения случая по умолчанию в этом сценарии?

Ответ №1:

Но, насколько я понимаю, я ясно дал понять, что A будет субъектом или расширением субъекта. Итак, почему Java ведет себя таким образом?

Да, A будет Actor или подкласс Actor — но это неправильное направление наследования для того, что вы хотите.

Предположим , у нас есть Role<SpecialistActor> . Затем constructActor() следует вернуть a SpecialistActor … и хотя a SpecialistActor является an Actor , равнина Actor (как в результате new Actor() ) не является a SpecialistActor . Это все равно что пытаться вернуть ссылку на экземпляр Object в методе, который объявлен для возврата String :

 public String constructString() {
    return new Object();
}
 

Вы же не ожидаете, что это скомпилируется, верно? Поэтому примените те же рассуждения к вашему примеру.

Ответ №2:

A может быть Actor или подклассом (или субинтерфейсом) actor .

Предположим, вы пишете:

 Role<SubActor> role = new Role<> ();
SubActor actor = role.constructActor ();
 

Это должно пройти компиляцию (на основе определения вашего Role класса и его параметра generic type A ), но ваш constructActor() метод всегда возвращает an Actor , что не всегда a SubActor .

Если ваш constructActor() метод всегда возвращает экземпляр Actor , вам следует просто изменить его возвращаемый тип на Actor . Нет необходимости использовать A .