Reputation: 31
My main question is if I can bind a generic object two 2 types if the it is extended by e.g. 4 types. I do not really know how to ask this question without an example.
So i created a little example of a simple game in which Warriors or Rangers can equip different type of Weapons(OneHanded, TwoHanded, Melee, Ranged). Every Weapon has two attributes. So the e.g. the Weapon type Dagger extends Weapon implements OneHanded, Melee
.
Ranger class (can use two-handed, ranged Weapons):
private Weapon weapon;
public <T extends TwoHanded & Ranged> void equip(T weapon) {
this.weapon = (Weapon) weapon;
}
Warrior class (can use one-handed, two-handed, melee, ranged weapons):
private Weapon weapon;
public <T extends OneHanded & Melee & Ranged> void equip(T weapon) { //probably have to do this differently
this.weapon = (Weapon) weapon;
}
Bow and Dagger class:
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
}
The main problem here is (I think) that I do not know how to implement it that a warrior can equip different weapons with different attributes(e.g. bow(ranged, two-handed) or also dagger(melee, one-handed)) whereas the ranger has only one possibility. How can I workaround this problem?
Upvotes: 2
Views: 124
Reputation: 3519
The other answer directly addresses the issue in the question. As you can see, using this design, the best option for you is to end up with a bunch of equipXXX()
methods.
An alternative would be to use the decorator pattern.
Create abstract Weapon
and WeaponDecorator
to allow maximum flexibility for later adding new weapon types.
public abstract class Weapon {
...
}
public abstract class WeaponDecorator extends Weapon {
Weapon _weapon;
WeaponDecorator(Weapon weapon) {this._weapon = weapon;}
}
Convert various weapon types to act as weapon decorators:
public class OneHanded extends WeaponDecorator {
OneHanded(Weapon weapon) {
super(weapon);
}
}
public class Melee extends WeaponDecorator {
Melee(Weapon weapon) {
super(weapon);
}
}
and remove all generics from the Warrior
class too:
public class Warrior {
private Weapon weapon;
public void equip(Weapon weapon) {
this.weapon = weapon;
}
}
Now you can simply do:
Weapon w = new OneHanded(new Melee(new Dagger()));
Warrior warrior = new Warrior();
warrior.equip(w);
Here is a full example with code and more explanation.
EDIT:
If you choose this solution, the responsibility of checking the validity of selected weapon for a selected hero should also be addressed in run time. For example, this can be added to Ranger
's equip
method:
if(weapon.isMelee()) //error (unacceptable)
But as the set of rules grow complex, you might want to use other patterns such as the command pattern. Still, all of this will be delegated to run time. This is the price that you pay for acquiring more flexibility. Of course, you can also try to acquire some compile-time safety by creating a hierarchy of decorators (similar to what java.io library does). However, this could make the application overly complicated very fast.
In the end, if there are only a few of this combined types (TwoHanded + Melee
, OneHanded + Ranged
, etc) it makes sense to go with the other answer and just have a few more equip
methods and have the safety of compile time type checking.
Upvotes: 0
Reputation: 147154
The code does not compile because Dagger
does not implement Ranged
.
I think you mean Melee
or Ranged
. This could be written as overloads.
<T extends Melee & OneHanded> void equip(T weapon) {
<T extends Ranged & OneHanded> void equip(T weapon) {
Note change of order in order for the overload to have distinct erasures. However, it is much better to have distinct names rather than overload.
(Also, I'd use a layer of indirection instead of losing the type information with a base type.)
Upvotes: 1