Java泛型和继承递归

8
我发现以下使用泛型和继承的Java代码。我真的不理解以下代码片段的作用:
class A<B extends A<B>> {
   ...
}

这段代码是做什么用的?
(我从MapDB中的DBMaker获取了这个代码)

1
关于这个主题的一些学习材料:http://vyazelenko.com/2012/03/02/recursive-generics-to-the-rescue/ - vitro
4个回答

5

这个问题实际上包含两个部分:

1) 为什么要使用 B extends A

2) 在 B extends A<B> 中为什么要使用 A 作为泛型类型并将其赋值为 B

对于这些部分的答案如下:

1) 在这个例子中,类(A)是一个构建器类(名为DBMaker),因此大多数其方法返回一些类型,这些类型扩展了该构建器类的类型。这就解释了为什么B应该扩展A类。

2) 但是,如果我们隐藏第二部分中的...extends A<B>,我们将得到一个普通的class A<B>。所以A具有类型变量B的类型。因此,在...extends A<B>中标记A为具有类型变量B的类型A


3
为什么这个被踩了?我本来要发表我的观点,说我也可以做到这一点(和其他人做的一样……),但我没有找到一个真正的使用案例。而Andremoniy给出了这种结构的一个真实的使用案例。(+1) - Serge Ballesta

2

这说明A需要派生定义来完成一些工作:

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

   protected T instance;

   T getOne() {
       return instance;
   }
}

public class B extends A<B> {
   public B() {
       instance = this;
   }
}

public static void test() {
   B b = new B();
   b.getOne();
}

这在接口定义中最常用,其中一个想要明确使用实现接口的类的实例作为返回类型或参数,而不是使用接口本身:

public interface TimeSeries<T extends TimeSeries<T>> {
    T join(T ts);
}

public class DoubleTimeSeries implements TimeSeries<DoubleTimeSeries> {
    @Override
    public DoubleTimeSeries join(DoubleTimeSeries ts) {
        return null;
    }
}

1
这使得定义方法返回类型更加容易,例如:
class A<B extends A<B>> {
      public B getMe(){
            return (B) this;
      }
}

这告诉Java编译器你正在getMe()方法中返回类A的子类。
class C extends A<C> {

}

C c = new C();
c.getMe(); //returns C

1

所以我进行了一些测试来弄清楚这个问题,以下是我的测试用例,展示如何使用这样一个通用案例:

public class A<B extends A<B>> {
    int x = 10;
    B test;

    void printX() {
        System.out.println(x);
    }

    void setB(B b) {
        test = b;
    }

    void printB() {
        System.out.println(test);
    }
}

public class B extends A<B> {

}

public class Run {
    public static void main(String[] args) {
        A<B> test = new A<B>();
        B myB = new B();
        test.printX();
        test.setB(myB);
        test.printB();
        myB.printB();

    }
}

我希望代码能够自我解释。如果不能,请留下评论,我会尝试解释正在发生的事情。看最后一行,myB.printB(),我们会得到一个null,因为B还没有被设置为myB,只被设置为test。这证明我们可以在A类和B类中(事实上,在B类内部)进行无限递归。
我们可以这样说:
myB.test.printB();

这将会出现一个错误(空指针),但是表明我们现在可以从B类中访问A类中的test,并且我们可以无限递归,使用尽可能多的情况。所以A类有点像无限多个B类的包装器。这有意义吗?

当您给出负面评价时,请留下评论! - Arash Saidi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接