如何以编程方式在Java中执行功能检测?

如何以编程方式在Java中执行功能检测?,java,java-8,java-7,Java,Java 8,Java 7,我想编写一个java框架,它支持JRE7作为基线,但是如果在JRE8的上下文中运行(向上兼容??),它会利用JRE8的特性 (或者可能我有反向的想法……也就是说,JRE8是基线,但会优雅地降级以支持JRE7) Java是否提供了实现这一点的方法 我的尝试/示例: 我想我可以用类似于javascript特性检测的方式来解决这个问题,我可以通过编程测试我的Java8方法是否存在,如果它存在,可以通过编程方式调用它,否则就退回到Java7库 在海事组织,这是一个非常艰巨的办法。如果这个“功能切换/检测

我想编写一个java框架,它支持
JRE7
作为基线,但是如果在
JRE8
的上下文中运行(向上兼容??),它会利用
JRE8
的特性

(或者可能我有反向的想法……也就是说,
JRE8
是基线,但会优雅地降级以支持
JRE7

Java是否提供了实现这一点的方法

我的尝试/示例: 我想我可以用类似于
javascript
特性检测的方式来解决这个问题,我可以通过编程测试我的
Java8
方法是否存在,如果它存在,可以通过编程方式调用它,否则就退回到
Java7

在海事组织,这是一个非常艰巨的办法。如果这个“功能切换/检测”可以由
Java运行时环境处理,那就太好了。这是可能的,还是我在吃疯狂的药片

免责声明:我还没有解决编译问题,这将排除此解决方案的有用性(如果我使用
Java7
编译,我无法将
-参数添加到编译的类中)


首先,使用反射来确保常规代码和可选API之间没有代码依赖关系是正确的。由于在何时解析类和成员引用方面有不同的策略,因此在一个JVM上工作的非反射性惰性创建可能在另一个JVM上失败

但这会带来一个问题,即当每个API使用都必须通过反射进行编码时,某些操作很难实现,特别是当您放松编译时检查时,运行时性能也可能会受到影响

一般的解决方案是使用Java从一开始就提供的OO技术。创建定义特征的
接口
抽象类
。实现Java8解决方案和Java7回退。在应用程序初始化期间或第一次需要该功能时,您需要进行一次反思性检查,检查您的最佳解决方案所依赖的功能是否可用,如果可用,则实例化最佳实现,否则实例化回退。从那时起,您可以像使用普通对象一样通过接口使用实现

在简单的情况下,您可以将类的数量减少到两个,一个是定义特性并提供回退行为的基类,另一个是用其专用子类覆盖实现的专用子类

将Java8实现保存在单独的源文件夹中,并使用Java8编译器进行编译。其他代码将使用Java7编译器进行编译,以确保对Java8实现没有依赖关系

通用Java 7兼容代码:

import java.lang.reflect.Method;

public class Index {
  static final MethodDescription DESC_ACCESSOR;
  static {
    MethodDescription md;
    try {
      Method.class.getMethod("getParameters");// test JRE feature
      // instantiate specialized solution
      md=(MethodDescription) Class.forName("MethodDescriptionJ8").newInstance();
    } catch(ReflectiveOperationException|LinkageError ex) {
      md=new MethodDescription(); // J7 fall-back
    }
    DESC_ACCESSOR=md;
  }

  void tellme(String yourname) {
      /* ... */
  }

  public static void main(String[] args) throws Exception {
      Method tellme = Index.class.getDeclaredMethod("tellme", String.class);
      String desc=DESC_ACCESSOR.get(tellme);
      System.out.println(desc);
  }
}
class MethodDescription {// base class defining application specific feature
    public String get(Method tellme) {// and providing fall-back implementation
      return String.format("(java7) %s %s(%s %s){/*...*/}",
          tellme.getReturnType(),
          tellme.getName(),
          tellme.getParameterTypes()[0].getName(),
          "arg0");
    }
}
使用Java 8单独编译:

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class MethodDescriptionJ8 extends MethodDescription {
    @Override
    public String get(Method tellme) {
      Parameter param = tellme.getParameters()[0];
      return String.format("(java8) %s %s(%s %s){/*...*/}",
                    tellme.getReturnType(),
                    tellme.getName(),
                    param.getType(),
                    param.getName());
    }
}

但是请注意,对于这个特定功能,结果可能令人失望。只有当内省类是使用带有
-parameters
标志的Java 8编译时,参数名才可用。因此,即使在运行时使用Java 8方法,检查Java 7兼容类也不会给出参数名称。

您可以利用Java classloader系统,只需要两个完整的应用程序,然后根据Java版本从
main
引导正确的版本。您可能会在Spring上寻找灵感,它似乎也在做类似的事情-因此在运行时,您试图确定什么语言级别(即Java 8、7等)代码被编译为?Java 8允许接口具有
default
方法和
static
方法。如果依赖
接口
作为交换Java 7和Java 8实现的一种方式,他们将无法在任何
接口
中使用
默认
静态
方法。如果我错了,请纠正我,但只是想知道这是否是使用
接口而不是
抽象类的限制。从Java 7开始,您可以将捕获到的所有异常压缩为一个
捕获(ReflectiveOperationException ex)
。Javadoc.@Chetan Kinger:基本类型必须能够用最低的通用平台进行编译,因此如果您以Java 7和Java 8为目标,如果您使用
接口作为基本类型,则
接口必须与Java 7兼容。使用
抽象类
还是
接口
的考虑与引入Java8之前相同。但是
接口
解决方案的另一个优点是,如果它只封装一个表示为与Java 8中的JRE方法匹配的一个方法的操作,那么您可以使用
LambdaMetaFactory
实现Java 8版本…
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

public class MethodDescriptionJ8 extends MethodDescription {
    @Override
    public String get(Method tellme) {
      Parameter param = tellme.getParameters()[0];
      return String.format("(java8) %s %s(%s %s){/*...*/}",
                    tellme.getReturnType(),
                    tellme.getName(),
                    param.getType(),
                    param.getName());
    }
}