在不知道Java中的基类的情况下对模板参数调用方法

在不知道Java中的基类的情况下对模板参数调用方法,java,templates,Java,Templates,我想写一个通用算法,可以用不同的对象实例化。这些对象来自第三方,它们没有公共基类。在C++中,我只是把泛型算法写为模板,它以特定对象作为参数。如何在Java中实现它 template <class T> class Algorithm { void Run(T& worker) { ... auto value = workder.DoSomething(someArgs); ... } }; 模板 类

我想写一个通用算法,可以用不同的对象实例化。这些对象来自第三方,它们没有公共基类。在C++中,我只是把泛型算法写为模板,它以特定对象作为参数。如何在Java中实现它

template <class T>
class Algorithm
{
    void Run(T& worker)
    {
        ...
        auto value = workder.DoSomething(someArgs);
        ...
    }
};
模板
类算法
{
无效运行(T&worker)
{
...
自动值=worker.DoSomething(someArgs);
...
}
};
在C++中,我不需要知道T的任何内容,因为在编译过程中检查了正确的类型和可用性。据我所知,, 在Java中,我必须有一个通用的基类,以便我的所有工作人员都能够对它们调用方法。是这样吗?有没有办法用Java做类似的事情

我不能改变我的第三方工人,我也不想对所有工人(包括工人正在使用的所有类型,等等)进行我自己的抽象

编辑:

由于我只想编写一次泛型算法,可能它是某种能够生成Java代码的模板语言的工作(代码模板的参数将是workers)

我的解决方案: 在我无法改变第三方工作人员的情况下,我选择了Java代码生成。我有完全相同的算法,我只需要支持不同的worker,它们都提供相同的接口(具有相同名称的类、相同方法名称等)。在少数情况下,我必须为特定的工作人员编写一些额外的代码

更清楚地说,我的“worker”实际上是一个专有数据库的访问层,每个worker对应一个数据库版本(它们是生成的)。
我目前的计划是使用FreeMaker之类的工具来生成多个Java源文件,每个DB版本一个,只有不同的导入

要为您研究的主题:

您可以像这样声明一个类

public class Whatever<T> {
公共类{
它使用允许任何引用类型的
T
。您不需要强制进一步“专门化”该T。但当然:在这种情况下,您只能在
T
的实例上从
Object
调用方法

如果你想调用一个更具体的方法,那么除了以某种方式描述该规范之外,没有其他方法了。因此,在你的例子中,合理的方法是至少引入一些核心接口


P>爪哇:没有一个“鸭子打字”,你不能只说有这个或那个方法,你总是需要一个类型,它必须是一个类或者一个接口。在java中不支持C++类型的鸭子打字。 作为备选方案,考虑:

  • 完全反射+使用
    对象
    -语法将非常糟糕,编译器不会帮助您进行编译检查
  • 支持一组预先已知的类型,并使用某种静态调度,例如一个大的
    开关
    /if-else-if块、一个类型->代码映射等。新类型将强制更改此代码
  • 在注释处理过程中完成的代码生成-您可以自动执行上述静态分派方法,或者可以为实现公共接口的每个受支持类型创建包装器类型。在编译过程中需要知道这些类型,新类型需要重新编译
编辑-用于代码生成和注释处理的资源:

  • 作者@sockeqwe
  • ,一个干净的Square代码生成工具

如果你真的没有办法用泛型正确地完成它,你可能需要使用反射

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);
}
A类{
公共字符串doIt(){
返回“完成!”;
}
}
B类{
公开日期doIt(){
返回Calendar.getInstance().getTime();
}
}
接口I{
公共对象doIt();
}
类IAdapter实现了I{
私人的最终目标是它;
公共IAdapter(对象it){
this.it=it;
}
@凌驾
公共对象doIt(){
//这是什么课。
Class itsClass=it.getClass();
//看看它的方法。
对于(方法m:itsClass.getMethods()){
//正确的方法名。
if(m.getName().equals(“doIt”)){
//公开该方法。
m、 setAccessible(true);
试一试{
//叫它。
返回m.invoke(it);
}捕获(例外e){
抛出新的RuntimeException(“`doIt`方法调用失败”,e);
}
}
}
//找不到该名称的方法。
抛出新的RuntimeException(“对象没有'doIt'方法”);
}
}
public void test()引发异常{
System.out.println(“你好,世界!”);
对象a=new-IAdapter(new a()).doIt();
对象b=newIAdapter(new b()).doIt();
System.out.println(“a=“+a+”b=“+b”);
}

但是,在使用反射之前,您应该尽一切努力使用普通的类型安全Java(如泛型)来解决这个问题。

在Java中,您的所有工作人员都必须有一个DoSomething方法(someArgs),这并不一定意味着它们扩展了相同的基类,相反,它们可以用这样的方法实现一个接口Worker。例如:

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);
}
public interface Worker {
    public Double DoSomething(String arg1, String arg2);
}
然后让不同的类实现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
    }
}
工人的另一种实施方式

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

也许这是一个糟糕的设计,你对类型一无所知?所以,据你所知,他们可能会通过一个测试