Java의 정적 메소드에서 getClass ()를 호출하는 방법은 무엇입니까?


350

정적 메소드가 있어야하는 클래스가 있습니다. 이 정적 메소드 내에서 getClass () 메소드를 호출하여 다음 호출을 수행해야합니다.

public static void startMusic() {
  URL songPath = getClass().getClassLoader().getResource("background.midi");
}

그러나 이클립스는 나에게 말한다 :

Cannot make a static reference to the non-static method getClass() 
from the type Object

이 컴파일 시간 오류를 해결하는 적절한 방법은 무엇입니까?


getResource()사용자 정의 (예 : 비 J2SE) 클래스의 인스턴스가 있기 전에 사용 하면 때때로 실패합니다. 문제는 JRE가 해당 단계에서 부트 스트랩 클래스 로더를 사용한다는 것인데, 이는 부트 스트랩 로더의 클래스 경로에 응용 프로그램 자원이 없습니다.
앤드류 톰슨

답변:


616

대답

TheClassName.class대신에 사용하십시오 getClass().

로거 선언

로그 선언을 쉽게 삽입 할 수 있도록 특정 유스 케이스에 많은 관심을 기울이기 때문에 그것에 대한 생각을 추가 할 것이라고 생각했습니다. 로그 프레임 워크는 종종 로그가 특정 컨텍스트 (예 : 정규화 된 클래스 이름)로 제한 될 것으로 예상합니다. 따라서 수정없이 복사 할 수 없습니다. 페이스트 안전 로그 선언에 대한 제안은 다른 답변에서 제공되지만 바이트 코드 팽창 또는 런타임 내부 검사 추가와 같은 단점이 있습니다. 나는 이것을 권장하지 않습니다. 복사-붙여 넣기는 편집자 문제이므로 편집기 솔루션이 가장 적합합니다.

IntelliJ에서는 라이브 템플릿을 추가하는 것이 좋습니다.

  • 약어로 "로그"를 사용하십시오.
  • private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger($CLASS$.class);템플릿 텍스트로 사용하십시오 .
  • 변수 편집을 클릭하고 표현식을 사용하여 클래스를 추가하십시오. className()
  • FQ 이름을 다시 포맷하고 줄이려면 확인란을 선택하십시오.
  • 컨텍스트를 Java : 선언으로 변경하십시오.

입력 log<tab>하면 자동으로 확장됩니다.

private static final Logger logger = LoggerFactory.getLogger(ClassName.class);

가져 오기를 자동으로 다시 포맷하고 최적화합니다.


6
잘못된 코드가 다른 클래스에서 실행될 때 복사 / 붙여 넣기 및 슬픈 슬픈 결과가 발생합니다.
윌리엄 Entriken

1
@WilliamEntriken : 익명의 내부 클래스를 사용하는 것은 그에 대한 훌륭한 해결책이 아니며 추가 클래스 파일로 바이트 코드를 부풀려 몇 초의 개발자 노력을 절약합니다. 사람들이 어디서나 복사 / 붙여 넣기를해야 할 곳이 무엇인지 잘 모르겠지만 언어 해킹이 아닌 편집기 솔루션으로 처리해야합니다. 예를 들어 IntelliJ를 사용하는 경우 클래스 이름을 삽입 할 수있는 라이브 템플릿을 사용하십시오.
Mark Peters

1
당신이 4 downvotes있어 난 정말 왜 궁금
알 - Mothafar을

"사람들이 어디서나 복사 / 붙여 넣기를해야 할 곳이 무엇인지 잘 모르겠습니다."- LogAndroid에서 사용하는 것과 같은 것 , 일반적으로 클래스 이름 과 같은 태그를 제공해야합니다. 그리고 아마도 다른 예가있을 것입니다. 물론 당신은 그것에 대한 필드를 선언 할 수 있지만 (일반적인 관행 임), 그것은 여전히 ​​복사하여 붙여 넣기입니다.
user149408

1
라이브 템플릿 솔루션에서 한 가지를 잊었습니다. "변수 편집"을 클릭하고 식에서 CLASS유형 을 입력하십시오 className(). 그러면 $ CLASS $가 실제로 클래스 이름으로 바뀌어야합니다. 그렇지 않으면 비어있게됩니다.
HerbCSO

122

질문의 코드 예제와 관련하여 표준 솔루션은 클래스를 이름으로 명시 적으로 참조하는 것이며 getClassLoader()호출 하지 않고 수행 할 수도 있습니다 .

class MyClass {
  public static void startMusic() {
    URL songPath = MyClass.class.getResource("background.midi");
  }
}

이 방법은 여전히 ​​뒷면에 코드를 복사하여 붙여 넣기 오류에 대해 안전하지 않다는 단점이 있습니다.이 코드를 여러 유사한 클래스에 복제해야하는 경우입니다.

헤드 라인의 정확한 질문에 대해서는 인접한 스레드에 게시 된 트릭이 있습니다 .

Class currentClass = new Object() { }.getClass().getEnclosingClass();

중첩 된 익명 Object서브 클래스를 사용 하여 실행 컨텍스트를 보유합니다. 이 트릭은 복사 / 붙여 넣기 안전이라는 이점이 있습니다 ...

다른 클래스가 상속하는 기본 클래스에서 이것을 사용할 때주의하십시오.

또한이 스 니펫이 일부 기본 클래스의 정적 메소드로 구성 currentClass되면 값은 해당 메소드를 사용하는 서브 클래스가 아니라 항상 해당 기본 클래스에 대한 참조가됩니다.


23
지금까지 가장 좋아하는 답변. NameOfClass.class는 분명하지만 클래스 이름을 알아야합니다. getEnclosingClass 트릭은 복사-붙여 넣기에 유용 할뿐만 아니라 내부 검사를 사용하는 정적 기본 클래스 메소드에도 유용합니다
Domingo Ignacio

1
우연히와 Class.getResource()는 약간 다르게 작동 할 수 있습니다 ClassLoader.getResource(). 참조 stackoverflow.com/a/676273
augurar

68

Java7 +에서는 정적 메소드 / 필드에서이를 수행 할 수 있습니다.

MethodHandles.lookup().lookupClass()

기본 메소드 에서 도움말을 인쇄하기 위해 getSimpleName () 호출 만 필요한 경우 좋은 솔루션 입니다.
Dmitrii Bulashevich

6
매번 클래스 이름을 변경하지 않고 어디서나 로거를 추가 할 수있는 '사본 붙여 넣기'솔루션을 찾고있었습니다. 당신은 나에게 그것을 줬다 : private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
Julien Feniou

지원되는 최상의 솔루션은 안드로이드가 API 26, 즉 Android 8까지 지원하지 않는다는 단점입니다.
user149408

24

나는 이것과 직접 씨름했다. 좋은 트릭은 정적 컨텍스트에서 현재 스레드를 사용하여 ClassLoader를 얻는 것입니다. 이것은 Hadoop MapReduce에서도 작동합니다. 다른 메소드는 로컬에서 실행될 때 작동하지만 MapReduce에서 사용될 경우 널 InputStream을 리턴합니다.

public static InputStream getResource(String resource) throws Exception {
   ClassLoader cl = Thread.currentThread().getContextClassLoader();
   InputStream is = cl.getResourceAsStream(resource);
   return is;
}


11

getClass() 메소드는 다음 서명을 사용하여 Object 클래스에 정의됩니다.

공개 최종 클래스 getClass ()

로 정의되어 static있지 않으므로 정적 코드 블록 내에서 호출 할 수 없습니다. 자세한 내용은 다음 답변을 참조하십시오 : Q1 , Q2 , Q3 .

정적 컨텍스트에 있다면 클래스 리터럴 표현식을 사용하여 클래스를 가져와야하므로 기본적으로 다음과 같이해야합니다.

푸 클래스

이 유형의 표현식을 클래스 리터럴 이라고 하며 Java 언어 사양서에 다음과 같이 설명되어 있습니다.

클래스 리터럴은 클래스 이름, 인터페이스, 배열 또는 기본 유형과`. '로 구성된 표현식입니다. 그리고 토큰 클래스. 클래스 리터럴의 유형은 클래스입니다. 현재 인스턴스 클래스의 정의 클래스 로더에 의해 정의 된 이름 지정된 유형 (또는 void)에 대해 Class 오브젝트로 평가됩니다.

이 주제에 대한 정보는 클래스의 API 문서 에서 찾을 수도 있습니다 .


9

시도 해봐

Thread.currentThread().getStackTrace()[1].getClassName()

또는

Thread.currentThread().getStackTrace()[2].getClassName()

이 방법의 장점은 8 이전의 Android 버전 (API 26)에서도 작동한다는 것입니다. 색인 [1]을 사용하면 모든 로그 메시지가 ThreadAndroid 4.4.4에서 소스 로보고 됩니다. [2]Android와 OpenJRE 모두에서 올바른 결과를 제공하는 것 같습니다.
user149408

0

Utility 클래스가 있다고 가정하면 샘플 코드는 다음과 같습니다.

    URL url = Utility.class.getClassLoader().getResource("customLocation/".concat("abc.txt"));

CustomLocation-자원 내의 폴더 구조가 없으면이 문자열 리터럴을 제거하십시오.


-2

이와 같은 것을 시도하십시오. 그것은 나를 위해 작동합니다. 로그 (클래스 이름)

    String level= "";

    Properties prop = new Properties();

    InputStream in =
            Logg.class.getResourceAsStream("resources\\config");

    if (in != null) {
        prop.load(in);
    } else {
        throw new FileNotFoundException("property file '" + in + "' not found in the classpath");
    }

    level = prop.getProperty("Level");

1
이 스레드에 이미있는 동일한 답변을 왜 세 번 제공하는지 잘 모르겠습니다. 2011 년에 두 번, 2013 년에 한 번
Antares42

이 솔루션은 클래스 로거가 "resources \\ config"폴더에 액세스하여 클래스 Logg를로드 한 경우 작동합니다. 여러 클래스 로더가있는 일부 서버 (예 : 하나의 클래스 로더가 lib 폴더를로드하고 다른 클래스 로더가 앱 라이브러리 및 자원을로드하는)에서는 사실이 아닙니다. 이런 이유로이 답변에 대한 해결책은 우연의 일치로만 작동합니다!
Manuel Romeiro
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.