簡體   English   中英

在不知道Java中的基類的情況下,對模板參數調用方法

[英]Call method on template argument without knowing a base class in Java

我想寫一個通用算法,可以用不同的對象實例化。 這些對象來自3rdparty,並且沒有通用的基類。 在C ++中,我只是將通用算法編寫為模板,該模板將特定對象作為其參數。 用Java怎么做?

template <class T>
class Algorithm
{
    void Run(T& worker)
    {
        ...
        auto value = workder.DoSomething(someArgs);
        ...
    }
};

在C ++中,我不需要了解T,因為在編譯過程中會檢查方法的正確類型和可用性。 據我所知,在Java中,我必須有一個公共基類供所有工作人員使用,以能夠對其調用方法。 這樣對嗎? 有沒有辦法在Java中做類似的事情?

我無法更改自己的3rdparty工作人員,也不想對所有工作人員(包括工作人員使用的所有類型,等等)進行自己的抽象。

編輯:

由於我只想編寫一次通用算法,因此對於某些能夠生成Java代碼的模板語言來說,這可能是一件工作(代碼模板的參數將是工作程序)?

我的解決方案:在無法更改3rdparty工作人員的情況下,我選擇了Java代碼生成。 我有完全相同的算法,我只需要支持所有提供相同接口(名稱相同,方法名稱相同的類)的不同工作程序即可。 而且在少數情況下,我必須為特定的工作人員做一些額外的代碼。

更明確地說,我的“工作人員”實際上是對專有數據庫的訪問層,每個工作人員都使用一個數據庫版本(並已生成)。 我目前的計划是使用FreeMaker之類的東西來生成多個Java源文件,每個數據庫版本一個,每個文件只有不同的導入。

需要尋找的主題: 泛型

你可以聲明一個像

public class Whatever<T> {

它使用允許任何引用類型的T 您無需強制性地進一步“專門化”該T。 但是,當然可以:在這種情況下,您只能在T實例上從Object調用方法。

如果要調用更特定的方法,則別無選擇,只能以某種方式描述該規范。 因此,在您的情況下,合理的方法是至少引入一些核心接口。

換句話說:Java中沒有“鴨子輸入”。 您不能通過說具有此方法的對象來描述它。 您始終需要一個類型-類型必須是類或接口。

Java不支持鴨子類型。 它可以近似,但是您不會獲得C ++所慣用的便利或強大功能。

作為選項,請考慮:

  • 全面反射+使用Object -語法將很糟糕,編譯器將無法幫助您進行編譯檢查。
  • 支持一組已知的類型,並使用某種靜態調度,例如,大的switch / if-else-if塊,類型->代碼映射等。新類型將強制更改此代碼。
  • 在注釋處理過程中完成了代碼生成-您可以自動執行上述靜態調度方法,也可以為確實實現公共接口的每種受支持類型創建包裝器類型。 這些類型在編譯期間需要知道,新的類型需要重新編譯。

編輯-用於代碼生成和注釋處理的資源:

如果您真的沒有任何方法可以使用泛型正確完成,則可能需要使用反射。

class A {
    public String doIt() {
        return "Done it!";
    }
}

class B {
    public Date doIt() {
        return Calendar.getInstance().getTime();
    }
}

interface I {
    public Object doIt();
}

class IAdapter implements I {
    private final Object it;

    public IAdapter(Object it) {
        this.it = it;
    }


    @Override
    public Object doIt() {
        // What class it it.
        Class<?> itsClass = it.getClass();
        // Peek at it's methods.
        for (Method m : itsClass.getMethods()) {
            // Correct method name.
            if (m.getName().equals("doIt")) {
                // Expose the method.
                m.setAccessible(true);
                try {
                    // Call it.
                    return m.invoke(it);
                } catch (Exception e) {
                    throw new RuntimeException("`doIt` method invocation failed", e);
                }
            }
        }
        // No method of that name found.
        throw new RuntimeException("Object does not have a `doIt` method");
    }
}

public void test() throws Exception {
    System.out.println("Hello world!");
    Object a = new IAdapter(new A()).doIt();
    Object b = new IAdapter(new B()).doIt();
    System.out.println("a = "+a+" b = "+b);
}

但是,您應該在使用反射之前,使用普通的類型安全的Java(例如,泛型)來解決此問題。

在Java中,所有Worker都必須具有DoSomething(someArgs)方法,這不一定意味着它們擴展了相同的基類,而是可以使用這種方法來實現一個Worker接口。 例如:

public interface Worker {
    public Double DoSomething(String arg1, String arg2);
}

然后讓不同的類實現Worker接口:

一種Worker的實現

public class WorkerImplA implements Worker{
    @Override
    public Double DoSomething(String arg1, String arg2) {
        return null; // do something and return meaningful outcome
    }
}

工人的另一種實現方式

public class WorkerImplB implements Worker{
    @Override
    public Double DoSomething(String arg1, String arg2) {
        return null; // do something and return meaningful outcome
    }
}

不同的WorkerImpl類不需要使用這種方法擴展相同的公共基類,並且從JavaSE 8開始,接口可以在其定義的任何方法中具有默認實現

使用這種方法, 算法類如下所示:

public class Algorithm {

    private String arg1;
    private String arg2;

    public Algorithm(String arg1, String arg2){
        this.arg1 = arg1;
        this.arg2 = arg2;
    }

    public void Run(Worker worker){
        worker.DoSomething(arg1, arg2);
    }
}

暫無
暫無

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

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