繁体   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