2015-10-17 58 views
6

我发现它的仿制药PARAMS扩展本身(here)的仿制药。我不太明白。我怀疑开始时是错的,但没有人提出。我有这方面的一些问题:什么有关涉及继承Java泛型和泛型的用处延伸自

  1. 如何使用变种的泛型,你能给我一个例子吗?
  2. 这种泛型风格的好处或影响。

这里是从(here)挑选出的仿制药样式代码。

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

} 

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

} 

谢谢!

+1

采取有关的递归定义看[这个问题](http://stackoverflow.com/q/211143/4125191) 'Enum'。如果'getNewInstance'声明'T'作为它的返回类型 – RealSkeptic

+1

http://stackoverflow.com/q/17099185/2158288 – ZhongYu

回答

3

我认为你是在谈论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; 
} 

现在,我们要为ManWoman创造的建设者。我们可以为每个实现一个构建器,复制设置nameage属性的方法。但是,由于ManWoman继承自Human,我们可以有一个抽象HumanBuilder,并使我们的WomanBuilderManBuilder继承它。这是F-bound类型派上用场的地方。

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(); 
     } 
    } 
} 

这种方法节约了相当多行代码,特别是在现实世界使用情况。

正如预期的那样,使用的建设者不需要任何铸造:

​​
+1

建设者应该接受'Man'并在其构造:-D – Marco13

+0

@ Marco13这将是'BabyBuilder'一个'Woman'例如:P –

1

从你的链接代码,它看起来像基地和变体类认为返回引用到自己的类的对象的方法,我猜类似于辛格尔顿。

abstract class Base { 
    protected abstract Base getNewInstance(); 
} 

现在,如果您想要返回Base的子类实例,那么您的运气不好。这就是仿制药进来的地方。

class Variant<T extends Variant<T>> extends Base<T> { 
    protected Base<T> getNewInstance() { 
     return new Variant(); 
    } 
} 

关于实用性,我个人并没有真正看到任何。它不必要的复杂,可能会被重构为更可读的东西。

+3

你的第二个例子是有关的。因为它是现在,你获得无论从'T为您声明'基地'作为返回类型扩展变'好处。 –

0

请考虑以下示例。您需要编写一个算法,其中需要Iterable的任何类型T并返回这些对象的最小值。要通过类型定义的相对顺序T你需要或者通过ComparatorT或者,作为替代,需要类型T延伸Comparable<T>。这是很方便的选择,因为许多内置类型,例如String,Integer等已经做到了。

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; 
} 

这不完全是延伸你正在谈论的形式,而是密切相关。例如,通用扩展本身可能对树结构有用。