简体   繁体   English

有没有办法在 Java 中调用延迟实现?

[英]Is there a way to call a deferred implementation in Java?

I'm not sure if the terminology is exactly correct here.我不确定这里的术语是否完全正确。

I have a class with some static methods that are called by several other classes in some library code written long ago.我有一个 class 和一些 static 方法,这些方法在很久以前编写的一些库代码中被其他几个类调用。

The class with the static methods that are called by several other classes has a reference to a third party class that now has a version 2. class 与 static 方法(由其他几个类调用)具有对第三方 class 的引用,该第三方现在具有版本 2。

Is there a way I can code this class to do something like "when this static method is call, there will be something that you need to call to see which version of the library you should be using"?有没有办法我可以编写这个 class 来执行类似“当调用这个 static 方法时,你需要调用一些东西来查看你应该使用哪个版本的库”?

Such that when I pull this library code in to a new project as a jar file, I can include something in each project that will tell my class in the library with the static methods to use a specific version.这样,当我将此库代码作为 jar 文件拉入新项目时,我可以在每个项目中包含一些内容,这些内容将告诉库中的 class 使用特定版本的 static 方法。

Such that I don't need to provide a version1 and version2 of each of the classes in my library.这样我就不需要为我的库中的每个类提供 version1 和 version2。

Am I on the right track with deferred?我是否在延期的正确轨道上?

A lambda function maybe? lambda function 也许?

There are a few different solutions to your problem, depending on how your third-party library has changed.您的问题有几种不同的解决方案,具体取决于您的第三方库的更改方式。

I'm assuming version 2 of your third-party class has new methods that are not on version 1 that you need?我假设您的第三方 class 的第 2 版具有您需要的第 1 版中没有的新方法?

The simplest solution is to implement your class against version 2 of the library, but handling LinkageError (or more a more specific subclass, for instance NoSuchMethodError ), and reverting to version 1 calls if these errors are caught.最简单的解决方案是针对库的版本 2 实施 class,但处理LinkageError (或更具体的子类,例如NoSuchMethodError ),如果捕获到这些错误,则恢复到版本 1 调用。 You can also handle if it's a completely different class, by handling NoClassDefFoundError .如果它是完全不同的 class,您也可以通过处理NoClassDefFoundError来处理。

Alternatively, if performance isn't critical, you can also use reflection .或者,如果性能不重要,您也可以使用反射

Sort of.有点。 Java doesn't load a class unless you actually need it. Java 不会加载 class ,除非您确实需要它。 This means you can do something like this:这意味着您可以执行以下操作:

public String getFoo() {
  switch (versionRequired()) {
    case 1: return new Version1Impl().getFoo();
    default: return new Version2Impl().getFoo();
  }
}

boolean versionRequired() {
  // figure out which version is needed here.
  // if you're having trouble with this, you can use `Class.forName` to check
  // if a certain type is even available. Worst case scenario, you can
  // ask a class for its own bytecode and hash that. This is very tricky.
  return 1;
}

private static class Version1Impl {
  public String getFoo() {
    return new UseSomethingFromV1().getFoo();
  }
}

The clue with the weird 'redirect to an inner class' stuff here is that the inner class won't be loaded until the switch branch is taken, and as long as eg Version2Impl never loads, you also never get the error that it is referring to a class that doesn't exist (because it was designed for v2 of that library, but only v1 is available).这里奇怪的“重定向到内部类”的线索是内部 class 在切换分支被采用之前不会被加载,只要例如Version2Impl永远不会加载,你也永远不会得到它所指的错误到不存在的 class (因为它是为该库的 v2 设计的,但只有 v1 可用)。


NB: About that version detection scheme: The problem with hashing classes is that a class may not have changed between 2 major releases (you wanted it to be different but it will not be).注意:关于那个版本检测方案:散列类的问题是 class 可能在 2 个主要版本之间没有变化(你希望它不同,但它不会)。 Or, on the flip side, a minor point release update may have changed it (it is now different, but you did not want it to be).或者,另一方面,一个次要的版本更新可能已经改变了它(它现在不同了,但你不希望它如此)。 One solution is to hash a lot of classes, which helps with the first case (you want to notice changes), but makes the second worse.一个解决方案是 hash 很多类,这有助于第一种情况(您想注意更改),但会使第二种情况更糟。 You can do this with eg try (InputStream in = String.class.getResourceAsStream("String.class")) { - which works for any class.您可以使用例如try (InputStream in = String.class.getResourceAsStream("String.class")) { - 适用于任何 class。 Toss this inputstream through a hasher (search the web if you don't know how, this is a very common task, plenty of tutorials to find).通过哈希器折腾这个输入流(如果你不知道如何搜索 web,这是一个非常常见的任务,有很多教程可以找到)。

Many libraries have a supported way to ask them what version they are, but there is no standard, so check the docs of this library.许多库都有一种受支持的方式来询问它们是什么版本,但没有标准,因此请查看该库的文档。

For now, I've created a file based solution.现在,我已经创建了一个基于文件的解决方案。 Look for a file on the class path, if it is there, read what version is indicated from the file.在 class 路径上查找文件,如果存在,请从文件中读取指示的版本。 If there is no file, use the default (original) version.如果没有文件,请使用默认(原始)版本。

package com.nach.core.util.fhir.parser;

import java.io.File;

import org.hl7.fhir.instance.model.api.IBaseResource;

import com.nach.core.util.file.FileUtil;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.IParser;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class FhirJsonParser {

    private static final FhirContext CTX;
    
    private static final IParser PARSER;

    static {
        File fhirContextFile = FileUtil.getFile("/fhir-context.txt");
        if (fhirContextFile != null && fhirContextFile.exists()) {
            String fhirContext = FileUtil.getAsString(fhirContextFile).trim();
            if ("Dstu3".equals(fhirContext)) {
                log.info("USING DSTU3 FOR FHIR PARSING");
                CTX = FhirContext.forDstu3();
            } else if ("R4".equals(fhirContext)) {
                log.info("USING R4 FOR FHIR PARSING");
                CTX = FhirContext.forR4();
            } else {
                CTX = FhirContext.forDstu3();
            }
        } else {
            CTX = FhirContext.forDstu3();
        }
        PARSER = CTX.newJsonParser();
    }

    /**
     * Generate a class from a json string.
     */
    public static <T extends IBaseResource> T parse(String jsonString, Class<T> cls) {
        try {
            IParser parser = CTX.newJsonParser();
            parser.setStripVersionsFromReferences(false);
            CTX.getParserOptions().setStripVersionsFromReferences(false);
            IParser jsonParser = CTX.newJsonParser();
            T rtn = jsonParser.parseResource(cls, jsonString);
            return rtn;
        } catch (Exception exp) {
            throw new RuntimeException(exp);
        }
    }

    /**
     * Generate a json string from a class.
     */
    public static String serialize(IBaseResource resource) {
        String rtn = PARSER.encodeResourceToString(resource);
        return rtn;
    }
    
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM