我有一个PlayerCharacter类.PlayerCharacter可以扩展(例如,VampirePlayerCharacter vs WerewolfPlayerCharacter)
我有一个特质课.特性可以扩展(例如,代或Gnosis).
PlayerCharacter有一个方法,#withTrait(Trait)它将Trait添加到一个集合中.
PlayerCharacter有一个方法,#applyAllTraits()它循环遍历集合并将它们中的每一个应用于角色.
A VampirePlayerCharacter应该能够被赋予任何Trait适用于a的东西PlayerCharacter,以及任何Trait只适用于a的东西VampirePlayerCharacter.
所以我添加了一个泛型类型 Trait<PC extends PlayerCharacter>
因此,可以BasicTrait<PlayerCharacter>和Generation<VampirePlayerCharacter>
我的难题:
如果PlayerCharacter的特征集合是Collection<Trait<PlayerCharacter>>,则VampirePlayerCharacter无法添加Trait<VampirePlayerCharacter>到集合中.
如果PlayerCharacter是特征集合Collection<Trait<? extends PlayerCharacter>>,那么VampirePlayerCharacter可以添加Trait<VampirePlayerCharacter>到集合中.但是,PlayerCharacter不能再遍历这些特征,因为它们的类型是不确定的(它可能是Trait<PlayerCharacter>一个Trait<VampirePlayerCharacter>或一个Trait<WerewolfPlayerCharacter>或者......)
如果PlayerCharacter是特征集合Collection<Trait<? super PlayerCharacter>>,则VampirePlayerCharacter无法添加Trait<VampirePlayerCharacter>,因为VampirePlayerCharacter不是超类型PlayerCharacter
我觉得更专业的特性只需要在他们的应用方法中使用演员阵容,如果你不恰当地设置它们,他们会爆炸 - 但我确信这不是一个新奇的问题,我的头发宽广.而我无法绕过解决方案.
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);
}
}
Run Code Online (Sandbox Code Playgroud)
问题是您需要将继承保留为通用类型信息。
基本上你必须做类似的事情:
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> {...}
Run Code Online (Sandbox Code Playgroud)
不过,它开始变得非常笨重。您可以通过组合来稍微改善这种情况:
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());
}
}
Run Code Online (Sandbox Code Playgroud)
就我个人而言,我仍然觉得它很笨拙。如果您主要只是使用这些Trait来构造对象,您可能会考虑使用构建器或工厂,这样就不需要使用泛型。