2015-10-17 83 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; 
} 

這不完全是延伸你正在談論的形式,而是密切相關。例如,通用擴展本身可能對樹結構有用。