2009-12-05 3 views
1

나는 JLS 또는 JavaC 버그 (확실하지 않음)의 이상한 점을 발견했습니다. 다음 내용을 읽고 적절한 경우 JLS 통행 또는 Sun Bug ID를 설명하는 설명을 제공하십시오.오버로드 된 패키지 - 개인 메서드로 인해 컴파일 오류가 발생합니다. -이 오류는 JLS 기괴 또는 javac 버그입니까?

  1. API - - 생각 서블릿 API
  2. IMPL - - API를 구현을 정의 - 프레임 워크 API를 정의

    가정하자 나는 3 "모듈"의 코드로 인위적인 프로젝트가 톰캣 서블릿 컨테이너

  3. 생각

    : -
  4. 앱 응용 프로그램은 내가 여기

각 모듈의 클래스입니다 썼다 0

API - MessagePrinter.java

package api; 

public class MessagePrinter { 

    public void print(String message) { 
     System.out.println("MESSAGE: " + message); 
    } 
} 

API - MessageHolder.java (예, 그것이 "IMPL"클래스를 참조 - 더 나중에에)

package api; 

import impl.MessagePrinterInternal; 

public class MessageHolder { 

    private final String message; 

    public MessageHolder(String message) { 
     this.message = message; 
    } 

    public void print(MessagePrinter printer) { 
     printer.print(message); 
    } 

    /** 
    * NOTE: Package-Private visibility. 
    */ 
    void print(MessagePrinterInternal printer) { 
     printer.print(message);  
    } 

} 

IMPL - MessagePrinterInternal.java -이 클래스는 API 클래스에 따라 달라집니다. 이름에서 알 수 있듯이, 이것은 내 작은 프레임 워크의 다른 곳에서의 "내부"사용을위한 것입니다. 마지막으로

package impl; 

import api.MessagePrinter; 

/** 
* An "internal" class, not meant to be added to your 
* application classpath. Think the Tomcat Servlet API implementation classes. 
*/ 
public class MessagePrinterInternal extends MessagePrinter { 

    public void print(String message) { 
     System.out.println("INTERNAL: " + message); 
    } 
} 

, 앱 모듈의 유일한 클래스 ... MyApp.java

import api.MessageHolder; 
import api.MessagePrinter; 

public class MyApp { 

    public static void main(String[] args) { 
     MessageHolder holder = new MessageHolder("Hope this compiles"); 
     holder.print(new MessagePrinter()); 
    } 

} 

그래서, 지금은 내 작은 응용 프로그램, MyApp.java을 컴파일 시도합니다. 내 API 항아리가 항아리, 예를 들어 api.jar를 통해 내보내졌고 좋은 시민이되었다고 가정합니다. Impl 클래스가 impl.jar에 포함되어 있지 않고 내 클래스 패스의 항아리 만 참조했습니다.

이제 API 클래스에는 "내부"구현 클래스에 대한 종속성이 없어야한다는 점에서 분명히 내 프레임 워크 디자인의 결함이 있습니다. 그러나 놀라운 점은 MyApp.java가 전혀 컴파일되지 않았다는 것입니다.

javac -cp api.jar src\MyApp.java 
src\MyApp.java:11: cannot access impl.MessagePrinterInternal class file for impl.MessagePrinterInternal not found 

    holder.print(new MessagePrinter()); 
       ^
     1 error 

컴파일러가 메서드 오버로드로 인해 print() 버전을 사용하려고하면 문제가 발생합니다. 그러나 컴파일 오류는 다소 예기치 않은데, 그 중 하나가 package-private이므로 MyApp에 표시되지 않습니다.

javac 버그입니까, 아니면 JLS의 이상한 점입니까?

컴파일러 : 썬 javac의 1.6.0_14

+1

"연산자 오버로드로 인해"는 "오버로드로 인해 메소드가 오버로드되어" – TofuBeer

+0

이 수정되었습니다. - 감사합니다. – noahlz

답변

1

JLS 또는 javac에는 아무런 문제가 없습니다. 물론 당신의 설명을 이해할 경우 클래스 MessageHolderMessagePrinterInternal을 참조하기 때문에 클래스 클래스가 컴파일되지 않기 때문에 컴파일되지 않습니다. 이 참조를 구현 (예 : API의 인터페이스)으로 분해해야합니다.

편집 1 : 설명을 위해 : 이것은 생각하는 것처럼 패키지가 표시되는 방법과는 관련이 없습니다. 문제는 컴파일을 위해 MessagePrinterInternal 유형이 필요하지만 classpath에는이 유형이 없다는 것입니다. javac이 참조 된 클래스에 액세스 할 수 없을 때 소스 코드를 컴파일 할 수는 없습니다.

EDIT 2 : 코드를 다시 읽었으며 다음과 같이 나타납니다. MyApp를 컴파일하면 MessageHolder 클래스를로드하려고 시도합니다. MessageHolder 클래스는 MessagePrinterInternal을 참조하므로로드하려고해도 실패합니다. 나는 그것이 JLS에 명시되어 있는지 확신하지 못한다. JVM에 의존 할 수도있다. Sun JVM을 사용해 본 경험이있는 경우, 클래스가로드 될 때 최소한 정적으로 참조 된 모든 클래스를 사용할 수 있어야합니다. 메소드 서명, 확장 클래스 및 구현 된 인터페이스의 모든 유형을 포함합니다. 이것은 반 직관적이라고 주장 할 수는 있지만 일반적으로 그러한 정보가 누락 된 클래스는 거의 없다고 응답합니다. 객체를 인스턴스화 할 수 없으며 메타 데이터 (클래스 객체) 등을 사용할 수 없습니다. 그 배경 지식, 나는 당신이 보는 행동이 예상된다고 말할 것입니다.

+0

예,하지만 그 방법은 MessagePrinterInternal은 package-private을 사용합니다. MyApp.java 클래스는 절대로 액세스 할 수 없습니다. 그래서 그것은 반 직관적입니다. – noahlz

+0

그것은 당신에게 반 직관적 일지 모르지만 나는 그가 옳다고 생각합니다. 그냥 시도해보십시오. –

+0

그의 대답은 간단하게 내 질문을 다시 말합니다. 물론 소스를 컴파일하기 위해 변경해야합니다. 내 질문 : JLS에서이 컴파일 실패를 예상 된 동작으로 정의하는 위치는 어디입니까? – noahlz

0

우선 내가 API를 패키지의 일이 클래스보다는 인터페이스 (이름 기준)으로 예상. 이 작업을 수행하면 인터페이스에서 패키지 액세스 권한을 가질 수 없으므로 문제가 해결됩니다.

다음은 AFAIK입니다. 이것은 Java가 이상합니다 (원하는대로 할 수 없다는 점에서). 공용 메소드를 제거하고 패키지를 비공개로 설정하면 같은 결과를 얻게됩니다.

api 패키지의 모든 것을 인터페이스로 변경하면 문제가 해결되고 코드가 더 깨끗하게 분리됩니다.

+0

인터페이스를 사용하도록이 코드를 변경했다하더라도 인터페이스 package-private을 선언하여 동일한 동작을 재현 할 수 있습니다. 예를 들어 MessagePrinterInternal은 API 패키지의 인터페이스가 될 수 있습니다. 사실,이 질문에 기초한 생산 코드는 정확히 그렇게합니다. – noahlz

+0

인터페이스가 패키지 인 경우 패키지 외부의 정보 만 볼 수 있습니다. 인터페이스에 코드를 작성하는 경우 (모든 변수와 매개 변수를 인터페이스로 선언하십시오) 아무런 문제가 없어야합니다. API라고하는 패키지에는 인터페이스, 열거 형, 팩토리 및 예외 이외의 것이 포함되어 있어도 이해가되지 않습니다. – TofuBeer

+0

미안하지만 당신이 말한 것에 기반을 둔다. 공개적으로 소비되는 것이기 때문에 API는 패키지를 가지고 있지 않아야한다. – TofuBeer

0

javac가 조금 더 똑똑 할 수 있다고 항상 주장 할 수 있지만 어딘가에서 멈춰야합니다. 그것은 인간이 아니며, 인간은 항상 컴파일러보다 더 똑똑 할 수 있습니다. 여러분은 언제나 인간에게 완벽한 의미를 갖는 예제를 찾을 수 있지만, 컴파일러를 어지럽히는 것입니다.

이 문제에 대한 정확한 사양을 모르겠습니다. javac 작성자가 실수를 저질렀습니다. 하지만 누가 신경 쓰냐? 그 중 일부가 피상적이라 할지라도 클래스 패스에 모든 의존성을 넣지 않는 이유는 무엇입니까? 일관되게 그 일을하면 의 삶이 훨씬 쉬워집니다.

+0

일부 클래스는 런타임에 사용해야하기 때문에 컴파일 클래스 패스에 있어야만하는 것은 아닙니다. 예를 들어 Sun 클래스는 Sun Java와 함께 제공됩니다. – noahlz

+0

예를 들어? 대부분의 태양 클래스는 컴파일 할 때 classpath에있는 rt.jar에 있습니다. – irreputable

+0

com.sun 클래스를 직접 사용하는 것은 최악의 방법으로 일반적으로 받아 들여집니다. http://java.sun.com/products/jdk/faq/faq-sun-packages.html – noahlz

관련 문제