2015-11-02 180 views
-2

請注意更新,我的問題沒有明確闡述。對不起。使用Java泛型的接口/抽象類的構造函數

讓我們假設我們有下面的代碼:

class Foo extends/implements AnAbstractClass/AnInterface { /* to make sure the constructor with int as input is implemented */ 
    Foo(int magicInt) { magicInt + 1; /* do some fancy calculations */ } 
} 

class Bar extends/implements AnAbstractClass/AnInterface { /* to make sure the constructor with int as input is implemented */ 
    Bar(int magicInt) { magicInt + 2; /* do some fancy calculations */ } 
} 

class Factory<T extends/implements AnAbstractClass/AnInterface> { 
    int magicInt = 0; 

    T createNewObject() { 
     return new T(magicInt) // obviously, this is not working (*), see below 
    } 
} 

/* how it should work */ 
Factory<Foo> factory = new Factory<Foo>(); 
factory.createNewObject() // => Foo with magicInt = 1 

Factory<Bar> factory = new Factory<Bar>(); 
factory.createNewObject() // => Bar with magicInt = 2 

(*)位置,我不知道該怎麼辦。我怎樣才能確保這樣的簽名的構造函數...(int magicInt)被實現?我不能定義

  1. 具有一定簽名的構造函數中的接口

    interface AnInterface { 
        AnInterface(int magicInt); 
    } 
    
  2. 一個抽象類,執行一定的構造

    abstract class AnAbstractClass { 
        abstract AnAbstractClass(int magicInt); 
    } 
    

    這顯然是缺失的要求在子類中實現構造函數:

    abstract class AnAbstractClass { 
        AnAbstractClass(int magicInt) {} 
    } 
    
  3. an interfaceabstract class一個靜態方法,它可以覆蓋爲AnInterfaceAnAbstractClass每個實現(我覺得一個工廠模式)

什麼是要走的路?

+0

我不明白你的問題是什麼......你究竟想達到什麼目的,爲什麼它不工作? –

+1

我認爲你想要的結果代碼有些奇怪。 'SampleSource'具有擴展'SampleFactory'的參數。然後在'getCurrentSample()'中調用這個示例工廠來創建一個與'SampleFactory'應該具有相同類型的示例。所以創建一個樣本給你一個樣本工廠? – Timo

+1

那麼因爲Java 8靜態方法在接口中是允許的。 – Flown

回答

1

這聽起來像是你真的在尋找一種解決方案,如何在沒有一堆if/else塊的情況下編寫一個通用的工廠方法,並且不需要在每個類中寫一個方法。因此,請考慮使用以下代碼中的反射:

interface Interface { 
} 

class Foo implements Interface { 
    Foo(int magicInt) { magicInt = magicInt + 1; /* do some fancy calculations */ } 
} 

class Bar implements Interface { 
    Bar(int magicInt) { magicInt = magicInt + 2; /* do some fancy calculations */ } 
} 

class Factory<T extends Interface> { 
    int magicInt = 0; 

    public T createNewObject(Class<T> typeToMake) { 
     try { 
      T t = createNewObjectWithReflection(typeToMake); 
      return t; 
     } catch (Exception e) { 
      throw new RuntimeException("Construction failed!", e); 
     } 
    } 

    private T createNewObjectWithReflection(Class<T> typeToMake) throws Exception { 
     // find the constructor of type to make with a single int argument 
     Constructor<T> magicIntConstructor = typeToMake.getDeclaredConstructor(Integer.TYPE); 
     // call the constructor with the value of magicInt 
     T t = magicIntConstructor.newInstance(magicInt); 
     return t; 
    } 
} 

/* Name of the class has to be "Main" only if the class is public. */ 
class Ideone 
{ 
    public static void main (String[] args) throws java.lang.Exception 
    { 
     Factory<Foo> fooFactory = new Factory<Foo>(); 
     Foo foo = fooFactory.createNewObject(Foo.class); 
     System.out.println(foo); 

     Factory<Bar> barFactory = new Factory<Bar>(); 
     Bar bar = barFactory.createNewObject(Bar.class); 
     System.out.println(bar); 
    } 
} 

您可以run the demo at IDEOne here

+0

是的,這是我正在尋找的解決方案。但是在Java中是醜陋的? –

+0

非常好,是的。我編輯了代碼帖子來整理代碼,但總體來說,反思並不常見。 – entpnerd

+0

工廠本身沒有通用性。在工廠中通用該方法就足夠了。我也認爲你可以使用TypeLiteral和Guice的Factory綁定來實現相同的功能,使用輔助注入。這使得你的代碼更清潔一些。 –

4

我真的沒有看到你的想法工作。

我覺得它打破了Factory模式的概念,該模式真正旨在讓負責創建單個類的實例的方法see ref

我寧願:

  1. 在你的工廠類一個方法,要構造
  2. 和可能,而不必在構造函數中的具體行爲每種類型的對象,有一個公共的構造父類抽象類和一個抽象方法,它可以做花式計算(但這真的是樣式偏好)。

這將導致沿着線的東西:

abstract class AbstractSample { 
    private int magicInt; 

    public AbstractSample(int magicInt) { 
     this.magicInt = magicInt; 
    } 

    protected int getMagicInt() { 
     return magicInt; 
    } 

    public abstract int fancyComputation(); 

} 

public class Foo extends AbstractSample { 
    public Foo(int magicInt) { 
     super(magicInt) 
    } 

    public int fancyComputation() { 
     return getMagicInt() + 1; 
    } 
} 

public class Bar extends AbstractSample { 
    public Bar(int magicInt) { 
     super(magicInt) 
    } 

    public int fancyComputation() { 
     return getMagicInt() + 2; 
    } 
} 

public class SampleFactory { 
    private int magicInt = 0; 

    public Foo createNewFoo() { 
     return new Foo(magicInt); 
    } 

    public Bar createNewBar() { 
     return new Bar(magicInt); 
    } 
} 

回答到以前的版本問題可能會被刪除的,如果更新的答案滿足OP

這是絕對奇怪的類都擴展Sample和執行SampleFactory ...

我寧願有沿着線的東西:

class Sample { 
    protected Sample() { /* ... */ } 
} 

interface SampleFactory<T extends Sample> { 
    T createSample(final int i); 
} 

class AccelerationSample extends Sample { 
    public AccelerationSample(final int i) { /* do some fancy int calculations*/ } 
} 

class OrientationSample extends Sample { 
    private OrientationSample (final int i) { /* do some fancy int calculations*/ } 
} 

abstract class SampleSource<T extends Sample> { 
    int magicInt; 
    SampleFactory<T> sampleFactory; 
    T getCurrentSample() { 
     return sampleFactory.createSample(magicInt); 
    } 
} 

class AccelerationSampleSource extends SampleSource<AccelerationSample> { 
    SampleFactory<AccelerationSample> sampleFactory = new SampleFactory<> { 
     public AccelerationSample createSample(final int i) { 
      return new AccelerationSample(i); 
     } 
    } 
} 

class OrientationSampleSource extends SampleSource<OrientationSample> { 
    SampleFactory<OrientationSample> sampleFactory = new SampleFactory<> { 
     public OrientationSample createSample(final int i) { 
      return new OrientationSample(i); 
     } 
    } 
} 

這將是清潔還是使用命名的工廠,如

public AccelerationSampleFactory implements SampleFactory<AccelerationSample> { 
    public AccelerationSample createSample(final int i) { 
     return new AccelerationSample(i); 
    } 
} 

然後你可以作爲

class AccelerationSampleSource extends SampleSource<AccelerationSample> { 
    SampleFactory<AccelerationSample> sampleFactory = new AccelerationSampleFactory(); 
} 
使用
+0

確實,很抱歉...無法在界面中定義靜態方法...我已經相應地更新了我的答案。 (如果你希望它是通用的,那麼不需要該方法是靜態的)。 – Thomas

+1

(更具體地說,在java 8中,你可以在接口中有靜態方法,但絕對不是靜態方法返回一個實例類型) – Thomas

+0

感謝你的解決方案,但是我有一個靜態工廠方法爲每個類我會有幾十個這樣的)。因此我認爲泛型被引入。 :) –

2

正如你所指出的那樣,這個問題中的3個想法都不被支持(一個帶有certa的構造函數在一個接口簽名,一個抽象類執行特定的構造函數,或靜態方法的接口或抽象類)

然而,可以定義的接口(或抽象類內)這是一個工廠的類型時最終想要。

public interface AnInterface { 
    int fancyComputation(); 
} 
public interface IFooBarFactory<T extends AnInterface> { 
    T create(int magicNumber); 
} 

IFooBarFactory具有2個具體實現

public class BarFactory implements IFooBarFactory<Bar> { 
    public Bar create(int magicNumber) { 
     return new Bar(magicNumber); 
    } 
} 
public class FooFactory implements IFooBarFactory<Foo> { 
    public Foo create(int magicNumber) { 
     return new Foo(magicNumber); 
    } 
} 

然後使用策略模式(https://en.wikipedia.org/wiki/Strategy_pattern)檢索正確的工廠。然後使用這個具有已知接口的工廠來製造具有正確值的對象(以及製造對象所需的任何附加值)。

FooBarFactory fooBarFactory = new FooBarFactory(); 
    IFooBarFactory<T> factory = fooBarFactory.createFactory(typeOfAnInterface); 
    T impl = factory.create(magicNumber); 

隨着conrete實現

public class Bar implements AnInterface { 
    private final int magicInt; 
    public Bar(int magicInt) { 
     this.magicInt = magicInt; 
    } 
    public int fancyComputation() { 
     return magicInt + 2; 
    } 
} 
public class Foo implements AnInterface { 
    private final int magicInt; 
    public Foo(int magicInt) { 
     this.magicInt = magicInt; 
    } 
    public int fancyComputation() { 
     return magicInt + 1; 
    } 
} 

以下代碼:

public static void main(String ... parameters) { 
    test(Foo.class); 
    test(Bar.class); 
} 
private static <T extends AnInterface> void test(Class<T> typeOfAnInterface) { 
    T impl = createImplForAnInterface(typeOfAnInterface, 10); 
    System.out.println(typeOfAnInterface.getName() + " produced " + impl.fancyComputation()); 
} 
private static <T extends AnInterface> T createImplForAnInterface(Class<T> typeOfAnInterface, int magicNumber) { 
    FooBarFactory fooBarFactory = new FooBarFactory(); 
    IFooBarFactory<T> factory = fooBarFactory.createFactory(typeOfAnInterface); 
    T impl = factory.create(magicNumber); 
    return impl; 
} 

打印

Foo produced 11 
Bar produced 12 

這提供了許多益處超過與內省ö的溶液r靜態工廠。調用者不需要知道如何製造任何對象,調用者也不需要知道或關心方法是何時使用「正確」方法來檢索正確類型。所有呼叫者只需呼叫一個公共/已知組件,即返回「正確」的工廠。這使得你的調用者更清潔,因爲他們不再與FooBar類型的AnInterface的具體實現緊密耦合。他們只需要關心「我需要實現AnInterface,它會消耗(或處理)這種類型。」我知道這意味着你有兩個「工廠」類。一個檢索正確的工廠,另一個實際負責創建具體類型Foo和Bar。但是,您可以通過附加的抽象層從調用方隱藏此實現細節(請參閱createImplForAnInterface方法)。

如果您通常使用某種形式的依賴注入,此方法將特別有用。我的建議與Guice的輔助注射(https://github.com/google/guice/wiki/AssistedInject)或春季的類似想法(Is it possible and how to do Assisted Injection in Spring?)完全相符。

這意味着你需要有幾個工廠類(或Guice的依賴注入綁定規則),但每個類都很小,很簡單,而且易於維護。然後編寫一個小測試,檢索實現AnInterface的所有類,並驗證實現策略模式的組件是否覆蓋了所有情況(通過反射 - 我將使用org.reflections中的Reflections類:reflections)。這爲您提供了一種可用的代碼抽象,通過減少冗餘代碼,放鬆組件的緊密耦合,而不是犧牲多態性來簡化這些對象的使用。