9

tl;博士

试图实现一个层次流畅的接口,以便我可以组合节点子类,同时也可以独立类,但获取类型参数不在其绑定错误之内。

细节

我正在尝试实现一个解决方案,以便我可以创建一些东西,以便我可以执行以下操作:

farm
    .animal()
        .cat()
            .meow()
            .findsHuman()
                .saysHello()
                .done()
            .done()
        .dog()
            .bark()
            .chacesCar()
            .findsHuman()
                .saysHello()
                .done()
            .done()
        .done()
    .human()
        .saysHello()
        .done();

同时还能够:

Human human = new Human()
    .saysHello()

我已经使用各种策略接近了,但无法获得所描述的灵活性。

我目前的尝试使用以下类:

abstract class Base<T extends Base<T>>{

    private T parent;

    Base(){

    }

    Base( T parent ){
        this.parent = parent;
    }

    public T done() throws NullPointerException{
        if ( parent != null ){
            return (T) parent;
        }

        throw new NullPointerException();
    }   
}

class Farm<T extends Base<T>> extends Base{

    private Animal<Farm<T>> animal;
    private Human<Farm<T>> human;

    public Farm(){
        super();
        this.animal = new Animal( this );
        this.human = new Human( this );
    }

    public Animal<Farm> animal(){
        return this.animal;
    }

    public Human<Farm<T>> human(){
        return this.human;
    }
}

class Animal <T extends Base<T>> extends Base{

    private Cat<Animal<T>> cat;
    private Dog<Animal<T>> dog;

    public Animal(){
        super();
        init();
    }

    public Animal( T parent ){
        super( parent );
        init();
    }

    private void init(){
        this.cat = new Cat(this);
        this.dog = new Dog(this);
    }

    public Cat<Animal<T>> cat(){
        return cat;
    }

    public Dog<Animal<T>> dog(){
        return dog;
    }
}

class Human<T extends Base<T>> extends Base{

    public Human<T> saysHello(){
        System.out.println("human says hi");
        return this;
    }
}

class Cat <T extends Base<T>> extends Base{

    private Human<Cat> human;

    public Cat(){
        super();
        init();
    }

    public Cat( T parent ){
        super( parent );
        init();
    }

    private void init(){
        this.human = new Human();
    }

    public Cat<T> meow(){
        System.out.println("cat says meow");
        return this;
    }

    public Human<Cat<T>> findsHuman(){
        return this.human;
    }
}


class Dog <T extends Base<T>> extends Base{

    private Human<Dog> human;

    public Dog(){
        super();
        init();
    }

    public Dog( T parent ){
        super( parent );
        init();
    }

    private void init(){
        this.human = new Human();
    }


    public Dog<T> bark(){
        System.out.println("dog says woof");
        return this;
    }

    public Dog<T> chacesCar(){
        System.out.println("cat drinks milk");
        return this;
    }

    public Human<Dog<T>> findsHuman(){
        return this.human;
    }

}

我看到的错误通常是:

Animal.java:4:类型参数Animal不在其绑定的私有Cat猫内;Animal.java:5:类型参数Animal不在其绑定的私有Dog狗内;

适用于所有类似的参考文献,也适用于我的示例所需案例:

找不到符号符号:方法 dog() 位置:类 Base.dog()

我尝试使用以下似乎可以解决类似问题的解决方案,但无济于事,因此欢迎任何和所有支持。

参考

4

7 回答 7

4

下面的代码似乎工作正常,不需要任何@SuppressWarnings. 要掌握的关键概念是,您的T参数实际上是对象父级的类,但T父级可以是任何东西。所以不是T extends Base<T>你想要的T extends Base<?>

输出是:

cat says meow
human says hi
dog says woof
cat drinks milk
human says hi
human says hi

...我认为这是正确的,尽管您可能想更改Dog.chacesCar()方法以使其不输出cat drinks milkchases也不应该chaces

希望这可以帮助!

abstract class Base<T extends Base<?>> {

    private final T parent;

    Base() {
        this.parent = null;
    }

    Base(T parent) {
        this.parent = parent;
    }

    public T done() throws NullPointerException {
        if (parent != null) {
            return parent;
        }

        throw new NullPointerException();
    }
}

class Farm<T extends Base<?>> extends Base<T> {

    private final Animal<Farm<T>> animal;
    private final Human<Farm<T>> human;

    public Farm() {
        super();
        this.animal = new Animal<>(this);
        this.human = new Human<>(this);
    }

    public Animal<Farm<T>> animal() {
        return this.animal;
    }

    public Human<Farm<T>> human() {
        return this.human;
    }
}

class Animal<T extends Base<?>> extends Base<T> {

    private Cat<Animal<T>> cat;
    private Dog<Animal<T>> dog;

    public Animal() {
        super();
        init();
    }

    public Animal(T parent) {
        super(parent);
        init();
    }

    private void init() {
        this.cat = new Cat<>(this);
        this.dog = new Dog<>(this);
    }

    public Cat<Animal<T>> cat() {
        return cat;
    }

    public Dog<Animal<T>> dog() {
        return dog;
    }
}

class Human<T extends Base<?>> extends Base<T> {
    public Human() {
        super();
    }

    public Human(T parent) {
        super(parent);
    }

    public Human<T> saysHello() {
        System.out.println("human says hi");
        return this;
    }
}

class Cat<T extends Base<?>> extends Base<T> {

    private Human<Cat<T>> human;

    public Cat() {
        super();
        init();
    }

    public Cat(T parent) {
        super(parent);
        init();
    }

    private void init() {
        this.human = new Human<>(this);
    }

    public Cat<T> meow() {
        System.out.println("cat says meow");
        return this;
    }

    public Human<Cat<T>> findsHuman() {
        return this.human;
    }
}

class Dog<T extends Base<?>> extends Base<T> {

    private Human<Dog<T>> human;

    public Dog() {
        super();
        init();
    }

    public Dog(T parent) {
        super(parent);
        init();
    }

    private void init() {
        this.human = new Human<>(this);
    }

    public Dog<T> bark() {
        System.out.println("dog says woof");
        return this;
    }

    public Dog<T> chacesCar() {
        System.out.println("cat drinks milk");
        return this;
    }

    public Human<Dog<T>> findsHuman() {
        return this.human;
    }

}

测试代码:

public static void main(String[] args) {
    Farm<?> farm = new Farm<>();
    farm
        .animal()
            .cat()
                .meow()
                .findsHuman()
                    .saysHello()
                    .done()
                .done()
            .dog()
                .bark()
                .chacesCar()
                .findsHuman()
                    .saysHello()
                    .done()
                .done()
            .done()
        .human()
            .saysHello()
            .done();

    Human human = new Human()
            .saysHello();
}
于 2014-10-15T10:00:19.637 回答
3

我想出的最好的事情是:

new Animal()
    .cat()
      .meow()
      .findsHuman()
        .<Cat>done()
      .<Animal>done()
    .dog()
      .bark()
        .findHuman()
          .<Dog>done()
      .done();

使用以下基类:

public abstract class Base<T extends Base<T>>{

  private Base<?> backRef;

  public Base() {}

  public Base(Base<?> backRef) {
    this.backRef = backRef;
 }

  @SuppressWarnings("unchecked")
    protected T self() {
    return (T)this;
  }

  @SuppressWarnings("unchecked")
  public <U extends Base<U>> U done() {
    return (U)backRef;
  }
}

如果您将 backRef 声明为 T 类型,则不允许使用其他类,因为它们不是彼此的子类,因此您必须指定不同的类型,但由于此类型是上下文相关的(一次是它的 Cat,一次是它的狗)我没有看到传递提示的替代方法。

我找到了一个解决方案:

new Animal()
    .cat()
      .meow()
      .findsHuman()
        .done()
      .done()
    .dog()
      .bark()
        .findHuman()
          .done()
    .done();



public abstract class Base<T extends Base<T,P>, P>{

  private P backRef;
  public Base() {}

  public Base(P backRef) {
    this.backRef = backRef;
  }

  @SuppressWarnings("unchecked")
  protected T self() {
    return (T)this;
  }

  public P done() {
    return backRef;
 }
}

就像有人建议的那样,我们为父级添加了一个额外的类型。

现在基类:

public final class Cat extends Base<Cat, Animal>{

  public Cat() {}

  public Cat(Animal backRef) {
    super(backRef);
  }

  public Cat meow() {
    System.out.println("Meeeoooww");
    return self();
  }

  public Human<Cat> findsHuman() {
    return new Human<Cat>(this);
  }
}

如您所见,Cat 清楚地指定了它应该使用的基本类型。现在对于人类,它可以根据上下文更改类型:

public final class Human<P> extends Base<Human<P>, P> {

  public Human() {}

  public Human(P backRef) {
    super(backRef);
  }

}

Human 指定了调用者(Cat、Dog)在其 findHuman() 方法中指定的附加泛型。

于 2014-10-14T13:59:40.653 回答
2

这是我们在一个项目中所做的:

public abstract class Parent<T extends Parent<T>> {

    /**
     * Get {@code this} casted to its subclass.
     */
    @SuppressWarnings("unchecked")
    protected final T self() {
        return (T) this;
    }

    public T foo() {
        // ... some logic
        return self();
    }

    // ... other parent methods

}

public class Child extends Parent<Child> {

    public Child bar() {
        // ... some logic
        return self();
    }

    // ... other child methods

}

允许孩子拥有自己的子类将是:

public class Child<T extends Child<T>> extends Parent<T> {

    public T bar() {
        // ... some logic
        return self();
    }

}
于 2014-10-14T13:41:06.113 回答
1

在这一行:

class Farm<T extends Base<T>>

编译器将第二个类型参数视为具体类。例如,假设您将那行替换为:

class Farm<T extends Base<Double>>

“Double”是一个具体的类。当编译器扫描它时,它无法区分 T 和 Double 之间的区别,并且将它们都视为具体类,而不是类型参数。让编译器知道 T 是类型参数的唯一方法是:

class Farm<T extends Base<T>, T>

我希望这能回答(或至少是相关的)你的问题。

编辑 帖子在我打字时被编辑,所以我想这个答案不再相关了。

于 2014-10-14T13:43:59.030 回答
1

您还可以使用接口,以便您可以伪造多重继承。有点冗长,但没有危险的演员表,我觉得这很容易理解。


定义可用的方法:

public interface AnimalIn {
    AnimalOut animal();
}

public interface CatIn {
    CatOut cat();
}

public interface MeowIn {
    CatOut meow();
}

public interface DogIn {
    DogOut dog();
}

public interface BarkIn {
    DogOut bark();
}

public interface ChacesCarIn {
    DogOut chacesCar();
}

public interface FindsHumanIn<T> {
    HumanOut<T> findsHuman();
}

public interface HumanIn {
    HumanOut<FarmOut> human();
}

public interface SaysHelloIn<T> {
    HumanOut<T> saysHello();
}

public interface DoneIn<T> {
    T done();
}

你可能需要在一个接口中有多个方法,但我还没有满足这个需求。例如,如果您不得不使用各种meows:

public interface MeowIn {
    CatOut meowForFood();
    CatOut meowForMilk();
    CatOut meowForStrokes();
}

定义输出类型:

Farm提供AnimalHuman

public interface FarmOut extends AnimalIn, HumanIn {
    // no specific methods
}

Animal提供Cat,DogDone:

public interface AnimalOut extends CatIn, DogIn, DoneIn<FarmOut> {
    // no specific methods
}

Cat提供Meow,FindsHumanDone:

public interface CatOut extends MeowIn, FindsHumanIn<CatOut>, DoneIn<AnimalOut> {
    // no specific methods
}

Dog提供Bark, ChacesCar,FindsHumanDone:

public interface DogOut extends BarkIn, ChacesCarIn, FindsHumanIn<DogOut>, DoneIn<AnimalOut> {
    // no specific methods
}

Human提供SayHelloDone

public interface HumanOut<T> extends SaysHelloIn<T>, DoneIn<T> {
    // no specific methods
}

只需实现 *Out 接口:

public class Farm implements FarmOut {

    @Override
    public AnimalOut animal() {
        return new Animal(this);
    }

    @Override
    public HumanOut<FarmOut> human() {
        return new Human<FarmOut>(this);
    }

}

public class Animal implements AnimalOut {

    private FarmOut chain;

    public Animal(FarmOut chain) {
        this.chain = chain;
    }

    @Override
    public CatOut cat() {
        return new Cat(this);
    }

    @Override
    public DogOut dog() {
        return new Dog(this);
    }

    @Override
    public FarmOut done() {
        return chain;
    }

}

public class Dog implements DogOut {

    private AnimalOut chain;

    public Dog(AnimalOut chain) {
        this.chain = chain;
    }

    @Override
    public DogOut bark() {
        System.out.println("bark");
        return this;
    }

    @Override
    public DogOut chacesCar() {
        System.out.println("chaces car");
        return this;
    }

    @Override
    public HumanOut<DogOut> findsHuman() {
        return new Human<DogOut>(this);
    }

    @Override
    public AnimalOut done() {
        return chain;
    }

}

public class Cat implements CatOut {

    private AnimalOut chain;

    public Cat(AnimalOut chain) {
        this.chain = chain;
    }

    @Override
    public CatOut meow() {
        System.out.println("meow");
        return this;
    }

    @Override
    public HumanOut<CatOut> findsHuman() {
        return new Human<CatOut>(this);
    }

    @Override
    public AnimalOut done() {
        return chain;
    }

}

public class Human<T> implements HumanOut<T> {

    private T chain;

    public Human(T chain) {
        this.chain = chain;
    }

    @Override
    public HumanOut<T> saysHello() {
        System.out.println("hello");
        return this;
    }

    @Override
    public T done() {
        return chain;
    }

}

这些实现在没有接口的情况下也可以工作:删除implements *Out@Overrides 并替换任何*Outby *(例如AnimalOutby Animal)。也就是说,使用接口更容易维护:只需更新它们并修复编译错误。找到带有接口的 DSL 解决方案也更容易(如您所见),它们有时只是必需的。


演示:

new Farm()
.animal()
    .cat()
        .meow()
        .findsHuman()
            .saysHello()
            .done()
        .done()
    .dog()
        .bark()
        .chacesCar()
        .findsHuman()
            .saysHello()
            .done()
        .done()
    .done()
.human()
    .saysHello()
    .done();

印刷:

meow
hello
bark
chaces car
hello
hello
于 2014-10-14T14:34:50.507 回答
1

您的问题是 done 方法应该返回父级,但父级不一定是 T,而只是一个 Base。另一个问题是,无论类是什么,done方法都应该总是返回同一个类。

但这里是您建议的课程的一个细微变化。首先Base声明它的具体类和它的具体父类:

abstract class Base<T extends Base<T, P>, P>{

    private P parent;

    Base(){

    }

    Base( P parent ){
        this.parent = parent;
    }

    public P done() throws NullPointerException{
        if ( parent != null ){
            return parent;
        }

        throw new NullPointerException();
    }   
}

完成后,派生的具体类变为:

class Farm extends Base<Farm, Object>{

    private Animal animal;
    private Human human;

    public Farm(){
        super();
        this.animal = new Animal( this );
        this.human = new Human( this );
    }

    public Animal animal(){
        return this.animal;
    }

    public Human human(){
        return this.human;
    }
}

class Animal extends Base<Animal, Farm>{

    private Cat cat;
    private Dog dog;

    public Animal(){
        super();
        init();
    }

    public Animal( Farm parent ){
        super( parent );
        init();
    }

    private void init(){
        this.cat = new Cat(this);
        this.dog = new Dog(this);
    }

    public Cat cat(){
        return cat;
    }

    public Dog dog(){
        return dog;
    }
}

class Human extends Base<Human, Farm>{

    public Human() {

    }

    public Human(Farm farm) {
        super(farm);
    }

    public Human saysHello(){
        System.out.println("human says hi");
        return this;
    }

}

class CatOrDog extends Base<Cat, Animal>{

    protected Human human;

    public CatOrDog(){
        super();
        init(null);
    }

    public CatOrDog( Animal parent ){
        super( parent );
        init(parent);
    }

    private void init(Animal parent){
        Animal parent = done();
        Farm farm = (parent == null) ? null : parent.done();
        this.human = new Human(farm);
    }

    public Human findsHuman(){
        return this.human;
    }
}


class Cat extends CatOrDog{

    public Cat(){
        super();
    }

    public Cat( Animal parent ){
        super( parent );
    }

    public Cat meow(){
        System.out.println("cat says meow");
        return this;
    }
}


class Dog extends CatOrDog {

    public Dog(){
        super();
    }

    public Dog( Animal parent ){
        super( parent );
    }

    public Dog bark(){
        System.out.println("dog says woof");
        return this;
    }

    public Dog chacesCar(){
        System.out.println("cat drinks milk");
        return this;
    }
}

有了它,我可以在没有任何错误或警告的情况下编写:

Farm farm = new Farm();
farm.animal()
    .cat()
        .meow()
        .findsHuman()
            .saysHello()
            .done()
        .animal()
    .dog()
        .bark()
        .chacesCar()
        .findsHuman()
            .saysHello()
            .done()
        .animal()
    .done()
.human()
    .saysHello()
    .done();

但请注意,我必须done用电话代替animals电话。

编辑 :

我添加了一个新类CatOrDog来分解Human处理。由于 a 的父级Human是 a ,因此如果存在正确的父级Farm,我会使用正确的父级初始化新的。human这样,不仅上述源代码编译时没有错误或警告,而且运行时也没有任何问题并打印:

cat says meow
human says hi
dog says woof
cat drinks milk
human says hi
human says hi
于 2014-10-14T14:40:02.993 回答
0

没有“安全”的方法可以做到这一点,但这应该编译:

class Dog extends Base{

 <T extends Dog> T bark(){
    return (T) this;
 } 

}
于 2014-10-14T13:47:03.417 回答