Common Java Fields

I have a PlayerCharacter class. PlayerCharacter can be extended (e.g. VampirePlayerCharacter vs WerewolfPlayerCharacter)

I have a Trait class. A trait can be expanded (for example, generation or gnosis).

PlayerCharacter has a #withTrait(Trait) method that adds Trait to the collection. PlayerCharacter has a #applyAllTraits() method that goes through the collection and applies each of them to a character.

A VampirePlayerCharacter should be provided with any Trait that can apply to PlayerCharacter , as well as any Trait that can only apply to VampirePlayerCharacter .

So, I added a generic type by creating Trait<PC extends PlayerCharacter>

So there could be BasicTrait<PlayerCharacter> and Generation<VampirePlayerCharacter>

My puzzle:

  • If the CharacterCharacter character set has Collection<Trait<PlayerCharacter>> , then VampirePlayerCharacter cannot add Trait<VampirePlayerCharacter> to the collection.

  • If PlayerCharacter collection of attributes Collection<Trait<? extends PlayerCharacter>> Collection<Trait<? extends PlayerCharacter>> , then VampirePlayerCharacter can add Trait<VampirePlayerCharacter> to the collection. However, PlayerCharacter can no longer PlayerCharacter over traits because their type is undefined (it can be Trait<PlayerCharacter> or Trait<VampirePlayerCharacter> or Trait<WerewolfPlayerCharacter> or ...)

  • If PlayerCharacter collection of attributes Collection<Trait<? super PlayerCharacter>> Collection<Trait<? super PlayerCharacter>> , then VampirePlayerCharacter cannot add Trait<VampirePlayerCharacter> , because VampirePlayerCharacter not a supertype of PlayerCharacter

I'm talking about the breadth of hair from the fact that more specialized features just have to use the cast in the method of applying them, and if you set them incorrectly, they will explode, but I'm sure this is not a new problem, and I just can’t wrap my head around the solution .

 class PlayerCharacter { private int str; List<Trait<?>> traits = new ArrayList<>(); PlayerCharacter withStrength(int str) { this.str = str; return this; } PlayerCharacter withTrait(Trait trait) { this.traits.add(trait); return this; } void applyTraits() { traits.forEach((Trait<?> t) -> t.apply(this)); } } class VampirePlayerCharacter extends PlayerCharacter { private int fangLength; VampirePlayerCharacter withFangLength(int fangLength) { this.fangLength = fangLength; return this; } } abstract class Trait<PC extends PlayerChracter> { void apply(PC pc); } class StrengthTrait extends Trait<PlayerCharacter> { private int str; StrengthTrait(int str) { this.str = str; } void apply(PlayerCharacter pc) { pc.withStrength(str); } } class FangLengthTrait extends Trait<VampirePlayerCharacter> { private int fangLength; FangLengthTrait(int fangLength) { this.fangLength = fangLength; } void apply(VampirePlayerCharacter pc) { pc.withFangLength(fangLength); } } 
+5
source share
1 answer

Well, the problem is that you need to keep inheritance as generic type information.

Basically, you will need to do something like:

 class PlayerCharacter<P extends PlayerCharacter<P>> { List<Trait<? super P>> myTraits; } class VampirePlayer extends PlayerCharacter<VampirePlayer> {...} abstract class Trait<P extends PlayerCharacter<P>> { abstract void apply(P player); } class FangLengthTrait extends Trait<VampirePlayer> {...} 

He begins to become very awkward. You can slightly improve the situation by approaching the composition:

 class Attributes {} class Base extends Attributes { int strength; } class Vampire extends Base { int fangLength; } class Player<A extends Attributes> { final A attributes; final List<Trait<? super A>> traits = new ArrayList<>(); Player(A attributes) { this.attributes = attributes; } void applyTraits() { for(Trait<? super A> t : traits) t.apply(this); } } interface Trait<A extends Attributes> { void apply(Player<? extends A> player); } class StrengthTrait implements Trait<Base> { @Override public void apply(Player<? extends Base> player) { player.attributes.strength = 1000; } } class FangLengthTrait implements Trait<Vampire> { @Override public void apply(Player<? extends Vampire> player) { player.attributes.fangLength = 100; } } final class Factory { private Factory() {} public static Player<Base> newPlayer() { return new Player<Base>(new Base()); } public static Player<Vampire> newVampire() { return new Player<Vampire>(new Vampire()); } } 

I still find it awkward, personally. If you mainly use these Trait to create objects that you might consider using a builder or factory, so you don't need to use generic files.

+2
source

Source: https://habr.com/ru/post/1208303/


All Articles