我需要支持两个版本的依赖关系,这两个版本具有相同的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
}
Greeter1Wrapper
和Greeter2Wrapper
实际上都在这里实现了这两个包的接口,但是由于签名是相同的,因此所有方法都转发到相应的委托实例。这些包装器是必需的,因为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")
可以对所有接口重复此模式。通过实现自定义的活动注释处理器,该处理器可以从单个注释生成所有必需的类,从而避免更多的样板。