如何以编程方式在 Java 中执行特征检测?

How do I programmatically perform feature detection in Java?

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

(或者也许我有这个倒退...即 JRE8 是基线,但优雅地降级以支持 JRE7)。

Java 是否提供了执行此操作的方法?

我的尝试/例子:

我想我可以用与 javascript 特征检测类似的方式来解决这个问题,我可以以编程方式测试我的 Java8 方法是否存在,如果它存在则以编程方式调用它,否则回退到Java7 个图书馆。

IMO 这是一个非常艰巨的方法。如果 Java Runtime Environment / Java Compiler 可以处理此“功能 toggling/detection”,那就太好了。这可能吗,还是我吃了疯狂的药?

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

public class Index {

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

    public static void main(String[] args) throws Exception {
        Method tellme = Index.class.getDeclaredMethod("tellme", String.class);
        Method java8Params = null;
        try {
            java8Params = Method.class.getMethod("getParameters");
        } catch (NoSuchMethodException t) { /* ignore */ }
        if (java8Params != null) {
            // java 1.8 !!
            Object param = ((Object[]) java8Params.invoke(tellme))[0];
            System.out.printf("(java8) %s %s(%s %s){/*...*/}",
                    tellme.getReturnType(),
                    tellme.getName(),
                    param.getClass().getMethod("getType").invoke(param),
                    param.getClass().getMethod("getName").invoke(param));
        } else {
            // java 1.7 ...
            System.out.printf("(java7) %s %s(%s %s){/*...*/}",
                    tellme.getReturnType(),
                    tellme.getName(),
                    tellme.getParameterTypes()[0].getName(),
                    "arg0");
        }
    }
}

首先,使用反射来确保您的通用代码与可选代码之间没有代码依赖关系是正确的API。由于关于何时解析 class 和成员引用允许不同的策略,因此碰巧在一个 JVM 上工作的非反射延迟创建可能在另一个 JVM 上失败。

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

一般的解决方案是从一开始就使用 Java 提供的 OO 技术。创建定义特征的 interfaceabstract class。实施 Java 8 解决方案和 Java 7 回退。在您的应用程序初始化期间或第一次需要该功能时,您可以 one 反射检查您的最佳解决方案所依赖的功能是否可用,如果是,则实例化最佳实现,但以其他方式实例化回退。从此以后,你就可以像普通对象一样通过接口使用实现了。

在简单的情况下,您可以将 class 的数量减少到两个,基础 class 定义功能并提供回退行为,以及专门的子 class用专门的实现覆盖实现。

将 Java8 实现保存在单独的源文件夹中,并使用 Java8 编译器编译它们。其他代码将使用 Java 7 编译器编译,确保不依赖于 Java 8 实现。

一般 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");
    }
}

使用 Java8:

单独编译
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());
    }
}

但请注意,关于此特定功能,结果可能会令人失望。参数 names 仅在内省 class 是使用带有 -parameters 标志的 Java 8 编译时可用。因此,即使在运行时使用 Java 8 方法,检查 Java 7 兼容 classes 也不会为您提供参数名称。