如果两个版本都需要支持,如何在Java中处理包名更改?

如果两个版本都需要支持,如何在Java中处理包名更改?,java,interface,xtend,Java,Interface,Xtend,我需要支持一个依赖项的两个版本,它们具有相同的API但包名不同 如何在不维护代码的两个版本的情况下处理此问题,唯一的更改是import语句 对于局部变量,我想我可以使用反射(丑陋!),但我使用有问题的类作为方法参数。如果我不想传递对象实例,我还能做些什么来从包名中提取 是否可以将自制接口(与API兼容)应用于现有实例,并将其作为该接口的实例传递 如果答案改变的话,我实际上主要是在代码中使用xtend。既然您使用的是xtend,下面是一个利用xtend的解决方案。不过,可能有更好的解决方案不基于X

我需要支持一个依赖项的两个版本,它们具有相同的API但包名不同

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

对于局部变量,我想我可以使用反射(丑陋!),但我使用有问题的类作为方法参数。如果我不想传递
对象
实例,我还能做些什么来从包名中提取

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


如果答案改变的话,我实际上主要是在代码中使用xtend。

既然您使用的是xtend,下面是一个利用xtend的解决方案。不过,可能有更好的解决方案不基于Xtend,这只适用于只包含与方法签名完全相同的接口的简单api

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

package vendor.api1

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")

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

由于您使用的是Xtend,这里有一个解决方案可以利用。不过,可能有更好的解决方案不基于Xtend,这只适用于只包含与方法签名完全相同的接口的简单api

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

package vendor.api1

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")

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

如果只是import语句,您可以考虑使用预处理器,这将根据某些标记替换正确的包。在创建新项目时,您不能将一个包导入另一个包吗?“我需要支持一个依赖项的两个版本,它们具有相同的API,但包名不同。”这似乎是一个XY问题。为什么?@Michael我正在为多个中间件编写代码生成器。对于其中一个,我需要支持多个API版本。这在前三个版本中运行良好,但第四个版本更新了一个第三方依赖项,该依赖项更改了包名。所以我不能只更新依赖项,我必须保持对旧版本的支持。仅仅说业务需求公平吗?@kutschkem为最新版本保留一个代码库,您可以像往常一样编译和JAR。然后用旧的包名重新编译源代码,并用限定符将其JAR。让依赖项目选择适用于它们的限定JAR。如果它只是import语句,您可能会考虑使用预处理器,这将根据某些标记替换正确的包。您是否可以在创建新项目时将一个包导入另一个包?“我需要支持一个依赖项的两个版本,它们具有相同的API,但包名不同。“似乎是个XY问题。为什么?@Michael我正在为多个中间件编写代码生成器。对于其中一个,我需要支持多个API版本。这在前三个版本中运行良好,但第四个版本更新了一个第三方依赖项,该依赖项更改了包名。所以我不能只更新依赖项,我必须保持对旧版本的支持。仅仅说业务需求公平吗?@kutschkem为最新版本保留一个代码库,您可以像往常一样编译和JAR。然后用旧的包名重新编译源代码,并用限定符将其JAR。让相关项目选择适用于他们的合格JAR。