Java泛型涉及继承和泛型自身的扩展有什么用处?

7
我发现泛型可以扩展自身的泛型参数 (这里),但我理解得不是很好。我怀疑一开始它就是错误的,但没有人提出异议。我有一些关于这个问题的疑问:
  1. 如何使用变体泛型?你能给我举个例子吗?
  2. 这种泛型风格的好处或作用是什么?

下面是从 (这里) 提取的泛型代码样式。

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

}

class Variant<T extends Variant<T>> extends Base<T> {

}

感谢您的信任!

1
看一下关于“Enum”递归定义的这个问题 - RealSkeptic
1
https://dev59.com/n3PYa4cB1Zd3GeqPhEzA - ZhongYu
3个回答

5
我认为你在谈论F-bounded types。在两个层次结构直接相关的情况下,我发现它们非常有用。我想到最清晰的例子是针对一组类的建造者模式。在这种情况下,也许有一个建造者层次结构会很有用。
以下是一个例子,可能会让你更好地理解。考虑以下(非常刻板印象的)层次结构:
public abstract class Human {

    protected String name;

    protected int age;
}

public class Woman extends Human {

    protected String loveMovie;
}

public class Man extends Human {

    protected String soccerTeam;
}

现在,我们希望为 Man 和 Woman 创建构建器。我们可以为每个人实现一个构建器,重复设置 name 和 age 属性的方法。然而,由于 Man 和 Woman 继承自 Human ,因此我们可以拥有一个抽象的 HumanBuilder ,并使我们的 WomanBuilder 和 ManBuilder 从中继承。这就是F-bounded类型派上用场的地方。 Human 类及其 HumanBuilder 如下:
public abstract class Human {

    protected String name;

    protected int age;

    public static abstract class HumanBuilder<H extends Human, 
                                              T extends HumanBuilder<H, T>> {
        protected String name;

        protected int age;

        @SuppressWarnings("unchecked")
        public T name(String name) {
            this.name = name;
            return (T) this;
        }

        @SuppressWarnings("unchecked")
        public T age(int age) {
            this.age = age;
            return (T) this;
        }

        protected void fill(H human) {
            human.name = this.name;
            human.age = this.age;
        }

        protected abstract H create();

        public final H build() {
            H human = this.create();
            this.fill(human);
            return human;
        }
    }
}

这将是Woman类,以及它的WomanBuilder:
public class Woman extends Human {

    protected String loveMovie;

    public static class WomanBuilder extends HumanBuilder<Woman, WomanBuilder> {

        protected String loveMovie;

        public WomanBuilder loveMovie(String loveMovie) {
            this.loveMovie = loveMovie;
            return this;
        }

        @Override
        protected void fill(Woman woman) {
            super.fill(woman);
            woman.loveMovie = this.loveMovie;
        }

        @Override
        protected Woman create() {
            return new Woman();
        }
    }
}

最后,这是Man类,以及它的ManBuilder:
public class Man extends Human {

    protected String soccerTeam;

    public static class ManBuilder extends HumanBuilder<Man, ManBuilder> {

        protected String soccerTeam;

        public ManBuilder soccerTeam(String soccerTeam) {
            this.soccerTeam = soccerTeam;
            return this;
        }

        @Override
        protected void fill(Man man) {
            super.fill(man);
            man.soccerTeam = this.soccerTeam;
        }

        @Override
        protected Man create() {
            return new Man();
        }
    }
}

这种方法可以节省很多代码行数,特别是在实际应用中。
使用构建器时不需要进行任何转换,符合预期:
Man man = new Man.ManBuilder()
    .name("Joe")
    .age(29)
    .soccerTeam("Los Angeles Galaxy")
    .build();

Woman woman = new Woman.WomanBuilder()
    .name("Jane")
    .age(25)
    .loveMovie("Meet Joe Black")
    .build();

2
е»әйҖ иҖ…еңЁе®ғ们зҡ„жһ„йҖ еҮҪж•°дёӯеә”иҜҘжҺҘ收дёҖдёӘManе’ҢдёҖдёӘWomanе®һдҫӢ :-D - Marco13
1
@Marco13 那就是 BabyBuilder :P - fps

1
从您提供的代码来看,Base和Variant类似乎持有返回指向其自身类对象的方法,我猜测这类似于单例模式。
abstract class Base {
    protected abstract Base getNewInstance();
}

现在,如果您想返回Base的子类实例,那么您就没办法了。这就是泛型发挥作用的地方。
class Variant<T extends Variant<T>> extends Base<T> {
    protected Base<T> getNewInstance() { 
        return new Variant(); 
    }
}

就实用性而言,我个人并不认为有什么用处。它过于复杂,可能需要重构为更易读的内容。


4
如果getNewInstanceT声明为其返回类型,你的第二个示例将是相关的。但是现在,由于您将Base<T>声明为返回类型,因此从T extends Variant<T>中获得不到任何好处。 - Mark Peters

0
考虑以下示例。您需要编写一个算法,该算法接受任何类型T的Iterable,并返回这些对象中的最小值。为了定义类型T上的相对顺序,您需要传递T上的Comparator或者要求类型T扩展Comparable作为替代方案。这是一种方便的替代方案,因为许多内置类型如String、Integer等已经实现了Comparable。
public static <T extends Comparable<T>> T min(Iterable<T> args) {
    T m = null;
    for (T arg : args)
        m = (m == null || arg.compareTo(m) < 0) ? arg : m;
    return m;
}

这不完全是你所谈论的扩展形式,但是它与之密切相关。通用扩展本身在树形结构中可能非常有用,例如。


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