2008-09-16 7 views
10

Java Swing 응용 프로그램을 유지 관리합니다.Java 컴파일 - 컴파일러에게 내 코드의 일부를 무시하도록 지시하는 방법이 있습니까?

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

코드는 자바 6 기능을 사용하는 3-4 클래스를 제외하고는 거의 동일합니다.

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

내 Java 컴파일러의 버전에 따라 내 코드의 일부를 주석 처리하거나 주석 처리를 제거하고 싶지 않습니다.

답변

4

클래스의 구현이 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 
} 

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

+0

Java 컴파일러의 문서화 된 동작입니까? –

+3

이 답변은 틀린 것 같습니다. 필자는 java.io.Console과 위의 접근법을 사용한 빠른 Java 응용 프로그램을 만들었습니다. 컴파일러가 1.5에서 다음과 같은 오류로 실패했습니다. Test.java:8 : 심볼을 찾을 수 없습니다. console() 위치 : 클래스 java.lang.System – noahlz

+0

1.6에서 컴파일하고 특정 1.5 JVM에서 실행될 때 런타임 기능. 물론, "유효하지 않은 부 버전 49.0"오류를 피하기 위해 1.5 JVM 바이트 코드 형식으로 다시 컴파일해야합니다. 이러한 접근 방식의 경우이 솔루션이 효과가 있습니다. – noahlz

4

내가 생각하기에 최선의 접근 방법은 아마도 빌드 스크립트를 사용하는 것입니다. 한 곳에서 모든 코드를 보유 할 수 있으며 포함 할 파일을 선택하고 포함시키지 않으므로 컴파일 할 코드 버전을 선택할 수 있습니다. 파일 당보다 세밀한 제어가 필요한 경우이 방법이 도움이되지 않을 수 있습니다.

1

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

적어도 Java 5 용 파일 버전과 Java 6 용 파일 버전이 있어야하며, 빌드 또는 작성을 통해 포함시켜야합니다. 그것을 하나의 큰 파일에 집어 넣고 컴파일러가 5를 이해하도록 이해하지 못하는 것들을 무시하도록하는 것은 좋은 해결책이 아닙니다.

HTH

- 니키 -

1

이 (ㅎ, 재미있는) 모든 자바 지지자 싫증이 나다 만들 것입니다하지만 난, C 프리 프로세서를 사용하여 내 소스 위해서 #ifdefs을 둘 것입니다. makefile, rakefile 또는 빌드를 제어하는 ​​것은 컴파일러에게 공급할 임시 파일을 만들기 위해 cpp를 실행해야합니다. 개미가이 일을 할 수 있는지 잘 모르겠습니다. 이 모든 답을 장소가 될 것 같은 유래가 보이지만

, 당신은 자바 지혜 이상에 http://www.javaranch.com에 아무도보고 배회하다을 wehn 수 있습니다. 나는이 질문이 오랜 시간 전 거기에서 다루어 졌다고 상상한다.

0

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

+0

하는 그는 출력 수정 된 소스 파일, 다음 해당에 자바 컴파일러를 실행할 것이다 실제 C 프리 프로세서를 실행의 C 컴파일러를 사용하는 제안입니다. 전부는 아니더라도 대부분의 C 컴파일러에는 전 처리기 만 실행하는 스위치가 있습니다. –

0

조건부 컴파일은 가능하지만 아주 좋지는 않습니다. javac은 도달 할 수없는 코드를 무시합니다. 따라서 코드를 올바르게 구조화 한 경우 컴파일러에서 코드의 일부를 무시하도록 할 수 있습니다. 이것을 올바르게 사용하려면 javac에 올바른 인수를 전달하여 도달 할 수없는 코드를 오류로보고하지 않아야합니다. :-)

2

JDK 5에서 빌드하는 "마스터"원본 루트 하나를 유지하십시오 JDK 6 또는 그 상위 버전에서 빌드해야하는 두 번째 병렬 소스 루트를 추가하십시오. 중복이 없어야합니다. 즉 두 클래스 모두에 클래스가 없습니다. 인터페이스를 사용하여 둘 사이의 진입 점을 정의하고 약간의 리플렉션을 정의하십시오. 당신은 등 IDE를 별도의 프로젝트를 다른의 JDK를 사용하여 이러한를 구성 할 수

---%<--- 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 6의 사용을 실수로 "누출"하기 쉽습니다 잘못된 파일에 저장하십시오.

Class.forName을 이와 같이 사용하는 대신 java.util.ServiceLoader (주 JDK 6을 사용할 수 있고 JDK 7에 대한 선택적 지원이 필요하면!), NetBeans Lookup, Spring 등.

더 새로운 JDK가 아닌 선택적인 라이브러리에 대한 지원을 생성하는 데 동일한 기술을 사용할 수 있습니다.

2

조건부 컴파일이 실제로 필요하지 않도록 코드를 리팩토링 할 수 있으며 조건부 클래스 로딩 만 가능합니다. 다음과 같은 내용이 있습니다.

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

} 

버전 별 코드의 수에 따라 많은 노력이 필요할 수 있습니다.

0

위에서 언급 한 public static final solution은 작성자가 언급하지 않은 또 다른 이점이 있습니다. 컴파일러는 컴파일 타임에이를 인식하고 if 문 내에서 참조하는 모든 코드를 컴파일합니다 그 최종 변수.

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

0

간단한 해결책이 될 수있다 :

  • 장소 일반 클래스 경로의 외부 분기 클래스.
  • 간단한 사용자 정의 클래스 로더를 작성하고 기본으로 main에 설치하십시오. 떨어져 cassloader가 (정상 시스템 클래스 로더)를 5/6 것들에 대한
  • 부모를 연기 할 수있는 5/6 사람의 모든 클래스에 대한
  • (부모에 의해 발견 될 수없는 유일한 사람이해야하는) 'os.name'속성 또는 자신의 속성을 통해 사용할 항목을 결정할 수 있습니다.
1

사용하려는 Java 6 기능에 따라 다릅니다.

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 
} 

이 코드는 자바 6로 컴파일되어야하지만, 모든 버전 (새로운 클래스가 참조되지 않음)로 실행할 수 있습니다 JTables에 행 분류기를 추가하는 등의 간단한 일을 위해, 당신은 실제로 실행시에 테스트 할 수 있습니다.

편집 : 더 정확하기 위해서는 (this page에 따라) 1.3 이후 모든 버전에서 작동합니다.

5

커스텀 클래스 로더와 동적으로 주석 처리 된 코드를 사용하는 것에 대한 제안은 유지 보수와 목초지를 새로 섞은 후에 불쌍한 영혼이 프로젝트를 선택하는 데있어서 다소 의문입니다.

해결책은 간단합니다. 영향을받는 클래스를 독립적 인 두 개의 독립적 인 프로젝트로 끌어냅니다. 패키지 이름이 동일하고 기본 프로젝트에서 사용할 수있는 jar 파일로 컴파일하십시오. 패키지 이름을 동일하게 유지하고 메소드 서명이 동일하고 아무런 문제가없는 경우 배포 스크립트에 필요한 버전의 jar 파일 만 삭제하십시오. 별도의 빌드 스크립트를 실행하거나 동일한 스크립트에서 별도의 타겟을 사용한다고 가정합니다. ant와 maven은 조건부로 파일을 가져 와서 복사 할 수 있습니다.

+1

독립적 인 프로젝트는 잔인하며 개별 소스 폴더와 맞춤 빌드 스크립트는 정상적으로 작동합니다. 공통 코드베이스와 함께 src/main에 대한 소스 폴더를 사용하고, 각 버전에 종속 된 기능에 대해서는 src/java5 및 src/java6을 사용하십시오. – Leonel

1

당신은 당신이 Java6에 독점적으로 컴파일을 모두 수행 한 후 조건부 Java5 또는 Java6 코드 경로 중 하나를 실행은 System.getProperty ("java.version")를 사용할 수 있습니다.

당신은 클래스에 Java6 전용 코드를 가질 수 있으며 클래스는 오랫동안 Java6 전용 코드 경로가 실행되지 않는 Java5에서 잘 실행됩니다.

는 모든 방법을 아주 새로운 자바 플러그인의 JVM까지 고대 MSJVM에서 실행되는 애플릿을 작성하는 데 사용되는 트릭입니다.

0

리플렉션 API를 사용할 수 있습니다. 모든 1.5 코드를 한 클래스에 넣고 1.6 API를 다른 클래스에 넣으십시오. 개미 스크립트에서는 1.6 용 클래스를 컴파일하지 않을 1.5 용 1.5 타겟과 1.5 용 클래스를 컴파일하지 않을 1.6 타겟을 만듭니다. 귀하의 코드에서 귀하의 자바 버전을 확인하고 해당 클래스를로드 반사 적절한 방식으로 javac 기능을 누락에 대해 불평하지 않습니다. 이것은 Windows에서 내 MRJ (Java Runtime Environment for Java) 응용 프로그램을 컴파일하는 방법입니다.

관련 문제