Многократные Обобщения в методе - PullRequest
3 голосов
/ 17 января 2020

Мой главный вопрос: могу ли я связать обобщенный объект c двумя 2 типами, если он расширен, например, 4 типами. Я действительно не знаю, как задать этот вопрос без примера.

Итак, я создал небольшой пример простой игры, в которой Воины или Рейнджеры могут экипировать различные типы оружия (OneHanded, TwoHanded, Melee, Ranged). , Каждое оружие имеет два атрибута. Например, тип оружия Dagger extends Weapon implements OneHanded, Melee.

класс рейнджера (можно использовать двуручное оружие):

    private Weapon weapon;

    public <T extends TwoHanded & Ranged> void equip(T weapon) {
        this.weapon = (Weapon) weapon;
    }

класс воина (можно использовать одноручное, двуручное оружие) , ближнее, дальнобойное оружие):

    private Weapon weapon;
    public <T extends OneHanded & Melee & Ranged> void equip(T weapon) { //probably have to do this differently
            this.weapon = (Weapon) weapon;
    }

Класс лука и кинжала:

public class Bow extends Weapon implements TwoHanded, Ranged {}
public class Dagger extends Weapon implements OneHanded, Melee {}

public void equipTest() {
    ranger.equip(bow);    //works fine
    warrior.equip(dagger); //does not work
}

Основная проблема здесь (я думаю) в том, что я не знаю, как реализовать это то, что воин может экипировать разное оружие с разными атрибутами (например, лук (дальний, двуручный) или также кинжал (рукопашный, одноручный)), тогда как у рейнджера есть только одна возможность. Как я могу обойти эту проблему?

Ответы [ 2 ]

2 голосов
/ 17 января 2020

Код не компилируется, потому что Dagger не реализует Ranged.

Я думаю, вы имеете в виду Melee или Ranged. Это может быть записано как перегрузка.

<T extends Melee & OneHanded> void equip(T weapon) {
<T extends Ranged & OneHanded> void equip(T weapon) {

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

(Кроме того, я бы использовал слой косвенности вместо потери информации о типе с базовым типом.)

0 голосов
/ 17 января 2020

Другой ответ напрямую касается вопроса в вопросе. Как вы можете видеть, используя этот дизайн, лучший вариант для вас - это использовать кучу equipXXX() методов.

Альтернативой может быть использование шаблона декоратора .

Создание абстрактных Weapon и WeaponDecorator для обеспечения максимальной гибкости при последующем добавлении новых типов оружия.

public abstract class Weapon {
...
}

public abstract class WeaponDecorator extends Weapon {
    Weapon _weapon;
    WeaponDecorator(Weapon weapon) {this._weapon = weapon;}
}

Преобразование различных типов оружия в качестве декораторов оружия:

public class OneHanded extends WeaponDecorator {
    OneHanded(Weapon weapon) {
        super(weapon);
    }
}

public class Melee extends WeaponDecorator {
    Melee(Weapon weapon) {
        super(weapon);
    }
}

и удалите все дженерики из класса Warrior:

public class Warrior  {
    private Weapon weapon;
    public void equip(Weapon weapon) {
        this.weapon = weapon;
    }
}

Теперь вы можете просто сделать:

Weapon w = new OneHanded(new Melee(new Dagger()));
Warrior warrior = new Warrior();
warrior.equip(w);

Вот полный пример с кодом и дополнительными пояснениями.

РЕДАКТИРОВАТЬ :

Если вы выберете это решение, ответственность за проверку достоверности выбранного оружия для выбранного героя также должна быть рассмотрена в время выполнения. Например, это можно добавить к методу Ranger equip:

if(weapon.isMelee()) //error (unacceptable)

Но поскольку набор правил становится сложным, вы можете использовать другие шаблоны, такие как команда рисунок . Тем не менее, все это будет делегировано во время выполнения. Это цена, которую вы платите за приобретение большей гибкости. Конечно, вы также можете попытаться добиться некоторой безопасности во время компиляции, создав иерархию декораторов (аналогично тому, что делает библиотека java .io). Однако это может сделать приложение слишком сложным и очень быстрым.

В конце концов, если есть только несколько из этих комбинированных типов (TwoHanded + Melee, OneHanded + Ranged и т. Д. c), имеет смысл go с другим ответом и просто есть еще несколько equip методов и безопасность проверки типов во время компиляции.

...