Java通用字段

Dre*_*ens 5 java generics

我有一个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)

Rad*_*def 3

问题是您需要将继承保留为通用类型信息。

基本上你必须做类似的事情:

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来构造对象,您可能会考虑使用构建器或工厂,这样就不需要使用泛型。