Java에서 익명의 내부 클래스를 정적으로 만들 수 있습니까?


123

Java에서 중첩 클래스는 둘 중 하나 일 수도 있고 static아닐 수도 있습니다 . 인 경우 static포함하는 인스턴스의 포인터에 대한 참조를 포함하지 않습니다 (더 이상 내부 클래스라고도 부르지 않고 중첩 클래스라고 함).

static참조가 필요하지 않을 때 중첩 클래스를 만드는 것을 잊으면 가비지 수집 또는 이스케이프 분석에 문제가 발생할 수 있습니다.

익명의 내부 클래스 static도 만들 수 있습니까? 아니면 컴파일러가이를 자동으로 파악합니까 (하위 클래스가 없기 때문에 가능함)?

예를 들어 익명의 비교기를 만들면 외부에 대한 참조가 거의 필요하지 않습니다.

  Collections.sort(list, new Comparator<String>(){
       int compare(String a, String b){
          return a.toUpperCase().compareTo(b.toUpperCase());
       }
  }

내부 클래스를 정적으로 만드는 것을 잊었을 때 "가비지 수집 또는 이스케이프 분석"의 문제점은 무엇입니까? 나는이 ... 단지 성능에 관한 생각
팀 쎄

17
내부 클래스 인스턴스는 필요하지 않더라도 외부 인스턴스에 대한 참조를 활성 상태로 유지합니다. 이것은 물건이 쓰레기 수거되는 것을 막을 수 있습니다. 어떤 것의 경량 인스턴스를 생성하는 (자원이 많은) 팩토리 객체를 상상해보십시오. 공장이 작업을 완료 한 후 (예 : 응용 프로그램 시작 중) 폐기 할 수 있지만 생성 한 항목이 다시 연결되지 않는 경우에만 작동합니다.
Thilo

알아요, 이것은 단지 예일 뿐이지 만 반복되는 Collections.sort(list, String.CASE_INSENSITIVE_ORDER)것이므로 Java 2부터 작동 한다고 언급해야합니다 . Collection API가 존재하기 때문에 읽어야합니다…
Holger

답변:


138

아니, 당신은 할 수 없으며 컴파일러는 그것을 알아낼 수 없습니다. 이것이 FindBugs static가 암시 적 this참조를 사용하지 않는 경우 항상 익명 내부 클래스를 명명 된 중첩 클래스 로 변경하도록 제안하는 이유 입니다.

편집 : Tom Hawtin-tackline에 따르면 익명 클래스가 정적 컨텍스트 (예 : main메서드)에서 생성되는 경우 익명 클래스는 실제로 static. 그러나 JLS는 동의하지 않습니다 .

익명 클래스는 절대 없습니다 abstract(§8.1.1.1). 익명 클래스는 항상 내부 클래스 (§8.1.3)입니다. 절대 아닙니다 static(§8.1.1, §8.5.1). 익명 클래스는 항상 암시 적입니다 final(§8.1.1.2).

Roedy Green의 Java Glossary에 따르면 익명 클래스가 정적 컨텍스트에서 허용된다는 사실은 구현에 따라 다릅니다.

코드를 유지하는 사람들을 당황하게 만들고 싶다면 언어 사양이 익명 클래스가 결코라고 말하지 않더라도 init 코드 및 메서드 javac.exe내에서 익명 클래스를 허용한다는 것을 wags가 발견했습니다 . 물론 이러한 익명 클래스는 개체의 인스턴스 필드에 액세스 할 수 없습니다. 나는 이것을 권장하지 않습니다. 이 기능 은 언제든지 가져올 수 있습니다.staticstaticstatic

편집 2 : JLS는 실제로 §15.9.2 에서보다 명시 적으로 정적 컨텍스트를 다룹니다 .

하자 C는 인스턴스화하는 클래스, 그리고하자 내가 인스턴스가 생성되는 수. 경우 C는 내부 클래스 다음 직접 둘러싸 인스턴스를 가질 수있다. i 의 즉시 둘러싸는 인스턴스 (§8.1.3)는 다음과 같이 결정됩니다.

  • 경우 C는 다음 익명 클래스입니다 :
    • 클래스 인스턴스 생성식이 정적 컨텍스트 (§8.1.3)에서 발생하면 i 에는 즉시 둘러싸는 인스턴스가 없습니다.
    • 그렇지 않으면 i 의 즉시 둘러싸는 인스턴스는 입니다 this.

따라서 정적 컨텍스트의 익명 클래스는 static기술적으로 static클래스가 아니지만 둘러싸는 클래스에 대한 참조를 유지하지 않는다는 점에서 중첩 클래스 와 거의 동일합니다 .


19
FindBugs에 대한 +1-모든 Java 개발자는 빌드에 이것을 가지고 있어야합니다.
Andrew Duffy

13
성능상의 이유로 거의 간결한 구문을 사용하지 않는 것이 좋습니다.
Thilo

2
JLS 3rd Ed는 정적 컨텍스트에서 내부 클래스의 경우를 다룹니다. JLS 의미에서는 정적이 아니지만 질문에 주어진 의미에서는 정적입니다.
Tom Hawtin-tackline

6
다음은 구현에 따라 달라지는 방법의 예입니다. 이 코드truejavac (sun-jdk-1.7.0_10) 및 falseEclipse 컴파일러를 사용하여 인쇄합니다 .
Paul Bellora 2013

1
@MichaelMyers 저는 FindBugs를 시뮬레이션하여 'this'참조를 사용하지 않고 Anonymous Inner를 수행하도록 경고했지만 아무 일도 일어나지 않았습니다. 답변의 시작 부분에서 말한 것처럼 FindBugs가 어떻게 경고하는지 보여줄 수 있습니까? 링크를 붙여 넣기 만하면됩니다.
Thufir Hawat 2015

15

거의. 정적 메서드에서 생성 된 익명의 내부 클래스는 외부 this에 대한 소스가 없기 때문에 분명히 효과적으로 정적입니다.

정적 컨텍스트의 내부 클래스와 정적 중첩 클래스 간에는 몇 가지 기술적 차이점이 있습니다. 관심이 있으시면 JLS 3rd Ed.


사실 나는 그것을 되 찾는다. JLS는 동의하지 않습니다. java.sun.com/docs/books/jls/third%5Fedition/html/… : "익명 클래스는 항상 내부 클래스이며 절대 정적이 아닙니다."
Michael Myers

1
질문에있는 것과는 다른 의미에서 정적입니다.
Tom Hawtin-tackline

1
나는 약간의 설명을 추가했습니다.
Tom Hawtin-tackline

15

나는 여기의 명명법에 약간의 혼란이 있다고 생각합니다. 이것은 너무 어리 석고 혼란 스럽습니다.

무엇이라고 부르든 이러한 패턴 (및 가시성이 다른 몇 가지 변형)은 모두 가능하고 정상적인 합법적 인 Java입니다.

public class MyClass {
  class MyClassInside {
  }
}

public class MyClass {
  public static class MyClassInside {
  }
}

public class MyClass {
  public void method() {
    JComponent jc = new JComponent() {
      ...
    }
  }
}

public class MyClass {
  public static void myStaticMethod() {
    JComponent jc = new JComponent() {
      ...
    }
  }
}

그것들은 언어 사양에서 제공됩니다 (정말로 신경 쓰이는 경우 정적 메서드 내부의 섹션 15.9.5.1 참조).

그러나이 인용문은 명백히 잘못되었습니다 .

javac.exe는 언어 사양에서 익명 클래스가 결코 정적이 아니라고 말하더라도 정적 초기화 코드 및 정적 메서드 내에서 익명 클래스를 허용합니다.

인용 된 저자가 static 키워드 와 static context를 혼동하고 있다고 생각합니다 . (당연히 JLS도이 점에서 약간 혼란 스럽습니다.)

솔직히, 위의 모든 패턴은 괜찮습니다 (당신이 "중첩", "내부", "익명"이라고 부르는 것 ...). 실제로, Java의 다음 릴리스에서이 기능을 갑자기 제거하는 사람은 없습니다. 솔직히!


2
"(당연히 JLS도이 점에서 약간 혼란 스럽습니다.)"당신이 맞았습니다. 구현에 따라 다르다는 말이 이상하게 들렸지 만 이전에 Java 용어집에서 명백한 오류를 본 적이 없었습니다. 이제부터는 한 알의 소금으로 가져갑니다.
Michael Myers

2
우리는 실제로 패턴에 대해 이야기하고 있지 않습니다. 익명의 중첩 클래스는 정적이라는 것을 의미합니다. 즉, 세 번째 예제에서 new와 사이에 "정적"을 추가합니다 JComponent.
Timmmm

원하는 것을 보여주기 위해 원래 질문에 설명을 추가했습니다.
Timmmm

@MichaelMyers, JLS의 받아쓰기는 항상 해석되어야합니다.
Pacerier


0

익명의 내부 클래스는 절대 정적이 아니지만 (정적 메서드 또는 최종 정적 필드가 아닌 필드를 선언 할 수 없음) 정적 컨텍스트 (정적 메서드 또는 정적 필드)에서 정의 된 경우에는 불가능하다는 점에서 정적으로 작동합니다. (정적 컨텍스트의 다른 모든 것과 마찬가지로) 둘러싸는 클래스의 비 정적 (즉 인스턴스) 멤버에 액세스


-3

익명의 내부 클래스를 정적 ​​메서드 내에서 호출하여 정적으로 만드는 것에 유의하십시오.

이것은 실제로 참조를 제거하지 않습니다. 익명 클래스를 직렬화하고 바깥 쪽 클래스를 직렬화 가능하게 만들지 않음으로써이를 테스트 할 수 있습니다.


5
-1 : 정적 메서드 내에서 익명 클래스를 만들면 실제로 외부 클래스에 대한 참조 제거됩니다. 익명 클래스를 직렬화하고 바깥 쪽 클래스를 직렬화 가능하게 만들지 않음으로써이를 테스트 할 수 있습니다. (방금했습니다.)
Christian Semrau 2011-06-24
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.