我需要支持两个版本的依赖关系,这两个版本具有相同的API,但包名称不同。

如何在不维护代码两个版本的情况下处理此问题,唯一的变化就是import语句?

对于局部变量,我想我可以使用反射(难看!),但是我将有问题的类用作方法参数。如果我不想传递Object实例,我还可以做些什么来从包名称中抽象出来?

是否有可能将与API兼容的自制接口应用于现有实例,并将其作为该接口的实例传递出去?

如果改变答案,我实际上实际上是将xtend用于我的代码。

最佳答案

由于您正在使用Xtend,因此这里是使用Xtend's @Delegate annotation的解决方案。也许有更好的解决方案不是基于Xtend的,并且这仅适用于仅由具有完全相同的方法签名的接口组成的简单API。

因此,假设您在不同的包中具有完全相同的方法签名的接口,例如像这样:

package vendor.api1

interface Greeter {
    def void sayHello(String name)
}


 

package vendor.api2

interface Greeter {
    def void sayHello(String name)
}


然后,您可以将两者组合成一个接口,而在代码中仅使用此组合接口。

package example.api

interface Greeter extends vendor.api1.Greeter, vendor.api2.Greeter {
}


到目前为止,这在Java中也是可行的,但是您必须为每种接口方法编写很多样板文件才能使其工作。在Xtend中,您可以使用@Delegate来自动生成所有内容,而不必关心接口有多少种方法或它们的外观:

package example.internal

import example.api.Greeter
import org.eclipse.xtend.lib.annotations.Delegate
import org.eclipse.xtend.lib.annotations.FinalFieldsConstructor

@FinalFieldsConstructor
class GreeterImpl implements Greeter {
    @Delegate val Api delegate
}

@FinalFieldsConstructor
class Greeter1Wrapper implements Greeter {
    @Delegate val vendor.api1.Greeter delegate
}

@FinalFieldsConstructor
class Greeter2Wrapper implements Greeter {
    @Delegate val vendor.api2.Greeter delegate
}


Greeter1WrapperGreeter2Wrapper实际上都在这里实现了这两个包的接口,但是由于签名是相同的,因此所有方法都转发到相应的委托实例。这些包装器是必需的,因为GreeterImpl的委托需要实现与GreeterImpl相同的接口(通常,如果包相同,则单个委托就足够了)。

现在,您可以在运行时决定要使用哪个版本。

val vendor.api1.Greeter greeterApi1 = ... // get from vendor API
val vendor.api2.Greeter greeterApi2 = ... // get from vendor API

val apiWrapper = switch version {
    case 1: new Greeter1Wrapper(greeterApi1)
    case 2: new Greeter2Wrapper(greeterApi2)
}

val example.api.Greeter myGreeter = new GreeterImpl(apiWrapper)
myGreeter.sayHello("world")


可以对所有接口重复此模式。通过实现自定义的活动注释处理器,该处理器可以从单个注释生成所有必需的类,从而避免更多的样板。

09-25 21:15