簡體   English   中英

使用 Java 泛型的接口/抽象類的構造函數

[英]Constructor to interface/abstract class using Java generics

請注意更新,我的問題沒有明確表述。 對不起。

讓我們假設我們有以下代碼:

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. 接口抽象類中的靜態方法,可以為AnInterfaceAnAbstractClass每個實現覆蓋(我想到了工廠模式)

要走的路是什么?

我真的不認為你的想法有效。

我覺得它打破了Factory模式的概念, Factory模式的真正目的是讓方法負責創建單個類的實例, 請參見 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();
}   

正如您所指出的,問題中的 3 個想法都不受支持(接口中具有特定簽名的構造函數、強制執行特定構造函數的抽象類或接口或抽象類中的靜態方法)

但是,您可以定義一個接口(或抽象類),它是您最終想要的類型的工廠。

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

使用具體實現

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

與具有內省或靜態工廠的解決方案相比,這提供了許多好處。 調用者不需要知道如何制造任何對象,調用者也不需要知道或關心什么時候方法是“正確”的方法來使用以檢索正確的類型。 所有調用者只調用一個公共/已知組件,它返回“正確”的工廠。 這使您的調用者更清晰,因為它們不再與 FooBar 類型的 AnInterface 的具體實現緊密耦合。 他們只需要關心“我需要一個 AnInterface 的實現,它使用(或處理)這種類型。” 我知道這意味着您有兩個“工廠”課程。 一個用於檢索正確的工廠,另一個實際上負責創建具體類型 Foo 和 Bar。 但是,您可以通過額外的抽象層(請參閱 createImplForAnInterface 方法)向調用方隱藏此實現細節。

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

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

聽起來您真的在尋找一種解決方案,以解決如何在沒有一堆 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);
    }
}

您可以在此處在 IDEOne 上運行演示

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM