Compilação Java - Existe uma maneira de dizer ao compilador para ignorar partes do meu código?

StackOverflow https://stackoverflow.com/questions/74171

  •  09-06-2019
  •  | 
  •  

Pergunta

Eu mantenho um aplicativo Java Swing.

Para compatibilidade retroativa com Java 5 (para máquinas Apple), mantemos duas bases de código, uma usando recursos do Java 6 e outra sem esses recursos.

O código é basicamente o mesmo, exceto pelas classes 3-4 que usam recursos do Java 6.

Desejo manter apenas 1 base de código.Existe uma maneira durante a compilação de fazer com que o compilador Java 5 'ignore' algumas partes do meu código?

Não desejo simplesmente comentar/descomentar partes do meu código, dependendo da versão do meu compilador java.

Foi útil?

Solução

Supondo que as classes tenham funcionalidade semelhante com 1,5 vs.6.0 diferenças na implementação, você pode mesclá-las em uma classe.Então, sem editar o código-fonte para comentar/descomentar, você pode contar com a otimização que o compilador sempre faz.Se uma expressão if for sempre falsa, o código da instrução if não será incluído na compilação.

Você pode criar uma variável estática em uma de suas classes para determinar qual versão deseja executar:

public static final boolean COMPILED_IN_JAVA_6 = false;

E então faça com que as classes afetadas verifiquem essa variável estática e coloquem as diferentes seções do código em uma instrução if simples

if (VersionUtil.COMPILED_IN_JAVA_6) {
  // Java 6 stuff goes here
} else {
  // Java 1.5 stuff goes here
}

Então, quando você quiser compilar a outra versão, basta alterar essa variável e recompilar.Isso pode aumentar o arquivo java, mas consolidará seu código e eliminará qualquer duplicação de código que você tenha.Seu editor pode reclamar de código inacessível ou algo assim, mas o compilador deve ignorá-lo alegremente.

Outras dicas

As sugestões sobre o uso de carregadores de classe personalizados e código comentado dinamicamente são um pouco incrédulas quando se trata de manutenção e preservação da sanidade de qualquer pobre alma que pegar o projeto depois de você voltar para novas pastagens.

A solução é fácil.Separe as classes afetadas em dois projetos separados e independentes - certifique-se de que os nomes dos pacotes sejam os mesmos e apenas compile em jars que você poderá consumir em seu projeto principal.Se você mantiver os nomes dos pacotes iguais e as assinaturas dos métodos iguais, não há problemas - basta colocar a versão do jar necessária em seu script de implantação.Eu presumo que você execute scripts de construção separados ou tenha alvos separados no mesmo script - ant e maven podem facilmente lidar com a captura condicional de arquivos e copiá-los.

Acho que a melhor abordagem aqui é provavelmente usar scripts de construção.Você pode ter todo o seu código em um único local e, ao escolher quais arquivos incluir e quais não incluir, você pode escolher qual versão do seu código compilar.Observe que isso pode não ajudar se você precisar de um controle mais refinado do que por arquivo.

Você provavelmente pode refatorar seu código para que a compilação condicional realmente não seja necessária, apenas o carregamento de classe condicional.Algo assim:

public interface Opener{

public void open(File f);

 public static class Util{
        public Opener getOpener(){
          if(System.getProperty("java.version").beginsWith("1.5")){
           return new Java5Opener();
          }
          try{ 
            return new Java6Opener();
           }catch(Throwable t){
            return new Java5Opener();
           }
        }
 }

}

Isso pode exigir muito esforço, dependendo de quantos trechos de código específicos da versão você possui.

Mantenha uma raiz de origem "mestre" criada no JDK 5.Adicione uma segunda raiz de origem paralela que deve ser construída no JDK 6 ou superior.(Não deve haver sobreposição, ou seja,nenhuma classe presente em ambos.) Use uma interface para definir o ponto de entrada entre os dois e um pouquinho de reflexão.

Por exemplo:

---%<--- main/RandomClass.java
// ...
if (...is JDK 6+...) {
    try {
        JDK6Interface i = (JDK6Interface)
            Class.forName("JDK6Impl").newInstance();
        i.browseDesktop(...);
    } catch (Exception x) {
        // fall back...
    }
}
---%<--- main/JDK6Interface.java
public interface JDK6Interface {
    void browseDesktop(URI uri);
}
---%<--- jdk6/JDK6Impl.java
public class JDK6Impl implements JDK6Interface {
    public void browseDesktop(URI uri) {
        java.awt.Desktop.getDesktop().browse(uri);
    }
}
---%<---

Você pode configurá-los como projetos separados em um IDE usando JDKs diferentes, etc.A questão é que a raiz principal pode ser compilada de forma independente e é muito claro o que você pode usar em qual raiz, enquanto que se você tentar compilar diferentes partes de uma única raiz separadamente, é muito fácil "vazar" acidentalmente o uso do JDK 6 nos arquivos errados.

Em vez de usar Class.forName assim, você também pode usar algum tipo de sistema de registro de serviço - java.util.ServiceLoader (se main pudesse usar JDK 6 e você quisesse suporte opcional para JDK 7!), NetBeans Lookup, Spring, etc.etc.

A mesma técnica pode ser usada para criar suporte para uma biblioteca opcional em vez de um JDK mais recente.

Na verdade não, mas existem soluções alternativas.Verhttp://forums.sun.com/thread.jspa?threadID=154106&messageID=447625

Dito isso, você deve manter pelo menos uma versão de arquivo para Java 5 e outra para Java 6 e incluí-los por meio de um build ou make conforme apropriado.Colocar tudo em um arquivo grande e tentar fazer com que o compilador 5 ignore coisas que ele não entende não é uma boa solução.

HTH

--nikki--

Isso fará com que todos os puristas de Java se estremeçam (o que é divertido, heh heh), mas eu usaria o pré-processador C, colocaria #ifdefs em meu código-fonte.Um makefile, rakefile ou o que quer que controle sua compilação, teria que executar cpp para criar arquivos temporários para alimentar o compilador.Não tenho ideia se a formiga poderia ser feita para fazer isso.

Embora o stackoverflow pareça que será o lugar para todas as respostas, você poderia quando ninguém estivesse olhando para http://www.javaranch.com para a sabedoria Java.Imagino que essa questão já tenha sido tratada lá, provavelmente há muito tempo.

Depende de quais recursos do Java 6 você deseja usar.Para algo simples como adicionar classificadores de linha a JTables, você pode testar em tempo de execução:

private static final double javaVersion =
         Double.parseDouble(System.getProperty("java.version").substring(0, 3));
private static final boolean supportsRowSorter =
         (javaVersion >= 1.6);

//...

if (supportsRowSorter) {
    myTable.setAutoCreateRowSorter(true);
} else {
    // not supported
}

Este código deve ser compilado com Java 6, mas pode ser executado com qualquer versão (nenhuma nova classe é referenciada).

EDITAR:para ser mais correto, funcionará com qualquer versão desde 1.3 (de acordo com esta página).

Você pode fazer toda a sua compilação exclusivamente em Java6 e então usar System.getProperty("java.version") para executar condicionalmente o caminho do código Java5 ou Java6.

Você pode ter código somente Java6 em uma classe e a classe funcionará bem em Java5, desde que o caminho do código somente Java6 não seja executado.

Este é um truque usado para escrever miniaplicativos que serão executados na antiga MSJVM até as novas JVMs de plug-in Java.

Não há pré-compilador em Java.Assim, não há como fazer um #ifdef como em C.Construir scripts seria a melhor maneira.

Você pode obter compilação condicional, mas não muito bem - o javac ignorará o código inacessível.Portanto, se você estruturou seu código corretamente, poderá fazer com que o compilador ignore partes de seu código.Para usar isso corretamente, você também precisa passar os argumentos corretos para javac para que ele não relate código inacessível como erros e se recuse a compilar :-)

A solução final estática pública mencionada acima tem um benefício adicional que o autor não mencionou - pelo que entendi, o compilador irá reconhecê-la em tempo de compilação e compilar qualquer código que esteja dentro de uma instrução if que se refira a essa variável final.

Então acho que essa é a solução exata que você estava procurando.

Uma solução simples poderia ser:

  • Coloque as classes divergentes fora do seu classpath normal.
  • Escreva um carregador de classe personalizado simples e instale-o em main como padrão.
  • Para todas as classes, exceto 5/6, o cassloader pode adiar para seu pai (o classloader normal do sistema)
  • Para os 5/6 (que devem ser os únicos que não podem ser encontrados pelo pai), ele pode decidir qual usar através da propriedade 'os.name' ou de sua preferência.

Você pode usar a API de reflexão.coloque todo o seu código 1.5 em uma classe e a API 1.6 em outra.Em seu script ant, crie dois alvos, um para 1.5 que não compilará a classe 1.6 e outro para 1.6 que não compilará a classe 1.5.em seu código, verifique sua versão java e carregue a classe apropriada usando reflexão, dessa forma o javac não reclamará da falta de funções.É assim que posso compilar meus aplicativos MRJ (Mac Runtime for Java) no Windows.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top