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

Nic*_*aly 5 java java-7 java-8

我想编写一个java框架,它支持JRE7作为基线,JRE8如果在JRE8(向上兼容??)的上下文中运行,则可以利用这些功能.

(或者我可能倒退了......即JRE8是基线,但是优雅降级以支持JRE7).

Java是否提供了这样做的方法?

我的尝试/例子:

我以为我可以用类似于javascript特征检测的方式解决这个问题,我可以通过编程方式测试我的Java8方法的存在,如果它以编程方式调用它,否则回退到Java7库.

IMO这是一个非常艰巨的方法.如果这个"特征切换/检测"可以由Java Runtime Environment/ 处理,那就太棒了Java Compiler.这可能吗,还是我服用疯狂药?

免责声明:我没有解决编译,这将排除此解决方案的有用性(如果我编译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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Hol*_*ger 6

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

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

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

在简单的情况下,您可以将类的数量减少到两个,定义特性和提供回退行为的基类,以及用其专用的类重写实现的专用子类.

将Java 8实现保留在单独的源文件夹中,并使用Java 8编译器进行编译.其他代码将使用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");
    }
}
Run Code Online (Sandbox Code Playgroud)

使用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());
    }
}
Run Code Online (Sandbox Code Playgroud)

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