Java 컴파일 - 내 코드의 일부를 무시하도록 컴파일러에 지시하는 방법이 있습니까?

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

  •  09-06-2019
  •  | 
  •  

문제

저는 Java Swing 애플리케이션을 유지 관리하고 있습니다.

Java 5(Apple 컴퓨터용)와의 하위 호환성을 위해 우리는 Java 6의 기능을 사용하는 코드베이스와 해당 기능이 없는 코드베이스를 유지합니다.

Java 6 기능을 사용하는 3~4개의 클래스를 제외하면 코드는 거의 동일합니다.

1개의 코드베이스만 유지하고 싶습니다.컴파일 중에 Java 5 컴파일러가 내 코드의 일부 부분을 '무시'하도록 하는 방법이 있습니까?

내 Java 컴파일러 버전에 따라 내 코드의 일부에 대해 단순히 주석을 달거나 주석을 제거하고 싶지 않습니다.

도움이 되었습니까?

해결책

클래스가 1.5와 1.5의 기능이 비슷하다고 가정합니다.6.0 구현의 차이점을 하나의 클래스로 병합할 수 있습니다.그런 다음 주석 처리/주석 해제를 위해 소스를 편집하지 않고도 컴파일러가 항상 수행하는 최적화에 의존할 수 있습니다.if 표현식이 항상 false인 경우 if 문의 코드는 컴파일에 포함되지 않습니다.

클래스 중 하나에 정적 변수를 만들어 실행할 버전을 결정할 수 있습니다.

public static final boolean COMPILED_IN_JAVA_6 = false;

그런 다음 영향을 받는 클래스에서 해당 정적 변수를 확인하고 간단한 if 문에 다양한 코드 섹션을 넣도록 합니다.

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

그런 다음 다른 버전을 컴파일하려면 해당 변수 하나만 변경하고 다시 컴파일하면 됩니다.Java 파일이 더 커질 수 있지만 코드를 통합하고 코드 중복을 제거합니다.편집자는 도달할 수 없는 코드에 대해 불평할 수 있지만 컴파일러는 이를 무시해야 합니다.

다른 팁

사용자 정의 클래스 로더와 동적으로 주석 처리된 코드를 사용하는 것에 대한 제안은 유지 관리와 새로운 목초지로 섞은 후 프로젝트를 선택하는 불쌍한 영혼의 온전한 보존과 관련하여 약간 믿기지 않습니다.

해결책은 쉽습니다.영향을 받는 클래스를 두 개의 별도의 독립 프로젝트로 가져옵니다. 패키지 이름이 동일한지 확인하고 기본 프로젝트에서 사용할 수 있는 jar로 컴파일하면 됩니다.패키지 이름을 동일하게 유지하고 메소드 서명을 동일하게 유지하면 문제가 없습니다. 배포 스크립트에 필요한 jar 버전을 삭제하기만 하면 됩니다.나는 당신이 별도의 빌드 스크립트를 실행하거나 동일한 스크립트에 별도의 대상을 가지고 있다고 가정합니다. ant와 maven은 모두 조건부로 파일을 잡고 복사하는 것을 쉽게 처리할 수 있습니다.

여기서 가장 좋은 접근 방식은 아마도 빌드 스크립트를 사용하는 것입니다.모든 코드를 한 위치에 보관할 수 있으며 포함할 파일과 포함하지 않을 파일을 선택하여 컴파일할 코드 버전을 선택할 수 있습니다.파일별 제어보다 더 세밀한 제어가 필요한 경우에는 도움이 되지 않을 수 있습니다.

조건부 컴파일이 실제로 필요하지 않고 조건부 클래스 로딩만 필요하도록 코드를 리팩터링할 수 있습니다.이 같은:

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();
           }
        }
 }

}

보유한 버전별 코드 조각 수에 따라 많은 노력이 필요할 수 있습니다.

JDK 5에서 빌드되는 하나의 "마스터" 소스 루트를 유지합니다.JDK 6 이상에서 빌드해야 하는 두 번째 병렬 소스 루트를 추가합니다.(겹침이 없어야 합니다.둘 다에는 클래스가 없습니다.) 인터페이스를 사용하여 둘 사이의 진입점과 약간의 반사를 정의합니다.

예를 들어:

---%<--- 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);
    }
}
---%<---

다른 JDK 등을 사용하여 IDE에서 이를 별도의 프로젝트로 구성할 수 있습니다.요점은 기본 루트를 독립적으로 컴파일할 수 있고 어떤 루트에서 무엇을 사용할 수 있는지 매우 명확하다는 것입니다. 반면 단일 루트의 여러 부분을 별도로 컴파일하려고 하면 실수로 JDK 6의 사용이 "누출"되기가 너무 쉽습니다. 잘못된 파일에.

이와 같이 Class.forName을 사용하는 대신 일종의 서비스 등록 시스템인 java.util.ServiceLoader(메인이 JDK 6을 사용할 수 있고 JDK 7에 대한 선택적 지원을 원하는 경우), NetBeans Lookup, Spring 등을 사용할 수도 있습니다.등.

동일한 기술을 사용하여 최신 JDK가 아닌 선택적 라이브러리에 대한 지원을 생성할 수 있습니다.

실제로는 아니지만 해결 방법이 있습니다.보다http://forums.sun.com/thread.jspa?threadID=154106&messageID=447625

즉, 최소한 Java 5용 파일 버전과 Java 6용 파일 버전을 하나씩 유지하고 적절하게 빌드 또는 make를 통해 포함해야 합니다.모든 것을 하나의 큰 파일에 집어넣고 5용 컴파일러가 이해하지 못하는 내용을 무시하도록 하는 것은 좋은 해결책이 아닙니다.

HTH

-- 니키 --

이것은 모든 Java 순수주의자들을 움츠러들게 만들 것입니다(재미있네요, ㅎ). 하지만 저는 C 전처리기를 사용하고 소스에 #ifdefs를 넣을 것입니다.makefile, rakefile 또는 빌드를 제어하는 ​​모든 것은 컴파일러에 공급할 임시 파일을 만들기 위해 cpp를 실행해야 합니다.개미가 이런 일을 하도록 만들어질 수 있을지는 모르겠습니다.

stackoverflow가 그럴 것처럼 보이지만 그만큼 모든 답을 얻을 수 있는 곳, 아무도 거기에 대해 우울해 보이지 않을 것입니다. http://www.javaranch.com 자바의 지혜를 위해.나는 이 질문이 아마도 오래 전에 그곳에서 다루어졌을 것이라고 생각합니다.

사용하려는 Java 6 기능에 따라 다릅니다.JTables에 행 분류기를 추가하는 것과 같은 간단한 작업의 경우 실제로 런타임에 테스트할 수 있습니다.

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
}

이 코드는 Java 6으로 컴파일해야 하지만 모든 버전에서 실행할 수 있습니다(새 클래스는 참조되지 않음).

편집하다:더 정확히 말하면 1.3 이후의 모든 버전에서 작동합니다(에 따르면). 이 페이지).

Java6에서만 모든 컴파일을 수행한 다음 System.getProperty("java.version")를 사용하여 조건에 따라 Java5 또는 Java6 코드 경로를 실행할 수 있습니다.

클래스에 Java6 전용 코드가 있을 수 있으며 Java6 전용 코드 경로가 실행되지 않는 한 클래스는 Java5에서 제대로 실행됩니다.

이는 고대 MSJVM에서 최신 Java 플러그인 JVM까지 실행되는 애플릿을 작성하는 데 사용되는 트릭입니다.

Java에는 사전 컴파일러가 없습니다.따라서 C에서처럼 #ifdef를 수행할 방법이 없습니다.빌드 스크립트가 가장 좋은 방법이 될 것입니다.

조건부 컴파일을 얻을 수 있지만 그다지 훌륭하지는 않습니다. javac는 도달할 수 없는 코드를 무시합니다.따라서 코드를 적절하게 구성했다면 컴파일러가 코드의 일부를 무시하도록 할 수 있습니다.이를 올바르게 사용하려면 javac에 올바른 인수를 전달하여 도달할 수 없는 코드를 오류로 보고하지 않고 컴파일을 거부해야 합니다. :-)

위에서 언급한 공개 정적 최종 솔루션에는 작성자가 언급하지 않은 추가 이점이 하나 있습니다. 제가 이해한 바에 따르면 컴파일러는 컴파일 타임에 이를 인식하고 해당 최종 변수를 참조하는 if 문 내에 있는 모든 코드를 컴파일합니다.

그래서 나는 그것이 당신이 찾고 있던 정확한 해결책이라고 생각합니다.

간단한 해결책은 다음과 같습니다.

  • 일반 클래스 경로 외부에 다양한 클래스를 배치하십시오.
  • 간단한 사용자 정의 클래스로더를 작성하고 이를 기본으로 기본으로 설치합니다.
  • 5/6 클래스를 제외한 모든 클래스에 대해 캐스로더는 상위 클래스(일반 시스템 클래스 로더)를 따를 수 있습니다.
  • 5/6 항목(부모가 찾을 수 없는 유일한 항목이어야 함)의 경우 'os.name' 속성을 통해 사용할지 아니면 사용자 고유의 속성을 사용할지 결정할 수 있습니다.

리플렉션 API를 사용할 수 있습니다.모든 1.5 코드를 한 클래스에 넣고 1.6 API를 다른 클래스에 넣으세요.개미 스크립트에서 1.6 클래스를 컴파일하지 않는 1.5용 대상과 1.5용 클래스를 컴파일하지 않는 1.6용 대상 두 개를 만듭니다.코드에서 Java 버전을 확인하고 리플렉션을 사용하여 적절한 클래스를 로드하면 javac가 누락된 기능에 대해 불평하지 않습니다.이것이 Windows에서 MRJ(Mac Runtime for Java) 애플리케이션을 컴파일하는 방법입니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top