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