왜 정적 중첩 인터페이스가 Java에서 사용됩니까?


235

코드베이스에서 정적 중첩 인터페이스를 찾았습니다.

class Foo {
    public static interface Bar {
        /* snip */
    }
    /* snip */
}

나는 이것을 전에 본 적이 없다. 원래 개발자가 접근 할 수 없습니다. 따라서 나는 이렇게 물어야한다.

정적 인터페이스의 의미는 무엇입니까? 를 제거하면 static어떻게됩니까? 왜 이런 짓을했을까요?


2
이것은 내부 인터페이스가 아니며 중첩 된 인터페이스입니다. 내부는 Java에서 특정 의미를 갖습니다.
Lorne의 후작

답변:


293

위 예제에서 static 키워드는 중복 적이며 (중첩 된 인터페이스는 자동으로 "정적"임) 의미에 영향을주지 않고 제거 할 수 있습니다. 나는 그것을 제거하는 것이 좋습니다. 인터페이스 메소드의 "public"과 인터페이스 필드의 "public final"도 마찬가지입니다. 수정자는 중복되며 소스 코드에 혼란을 더합니다.

어느 쪽이든 개발자는 단순히 Foo.Bar라는 인터페이스를 선언합니다. Foo에 액세스 할 수없는 코드가 Foo.Bar에 액세스 할 수 없다는 점을 제외하고는 둘러싸는 클래스와 더 이상 연결되지 않습니다. (소스 코드에서-Foo가 패키지 전용 인 경우에도 바이트 코드 또는 리플렉션이 Foo.Bar에 액세스 할 수 있습니다!)

외부 클래스에서만 사용할 것으로 예상되는 경우 중첩 된 인터페이스를 작성하여 새 최상위 이름을 작성하지 않는 것이 허용되는 스타일입니다. 예를 들면 다음과 같습니다.

public class Foo {
    public interface Bar {
        void callback();
    }
    public static void registerCallback(Bar bar) {...}
}
// ...elsewhere...
Foo.registerCallback(new Foo.Bar() {
    public void callback() {...}
});

1
Jesse Glick의 대답에서 이것은 무엇을 의미합니까? (소스 코드에서-바이트 코드 또는 리플렉션은 Foo가 패키지 전용 인 경우에도 Foo.Bar에 액세스 할 수 있습니다!)
Vasu

Kaillash 전용 메소드는 리플렉션 (반사 패키지)을 통해 생성 된 .class 파일의 바이트 코드에 직접 액세스하여 액세스 할 수 있습니다.
gmoore

2
"바이트 코드 ... Foo.Bar에 액세스 할 수 있습니다"는 Foo.Bar를 참조하는 컴파일 된 클래스를 Foo를 참조 할 수없는 경우에도로드하고 실행할 수 있음을 의미합니다. 이는 Foo가 공개 된 초기에 클래스가 컴파일되었거나 클래스가 Java 이외의 언어 등에서 수동으로 어셈블되거나 컴파일 된 경우에 발생할 수 있습니다. 그러나 Java 컴파일러는 둘러싸는 클래스의 액세스 수정자를 확인합니다. 결과 바이트 코드가 해당 둘러싸는 클래스를 참조하지 않는 경우에도 마찬가지입니다.
Jesse Glick

@Jesse 리플렉션을 통해 프라이빗 최상위 클래스의 프라이빗 정적 클래스에 액세스 할 수 있습니까?
Pacerier

1
@ 페이 서 : 아니오. 보다 정확하게는 클래스를로드하고 멤버를 검사 할 수 있지만 setAccessible (true)을 사용하지 않고 인스턴스를 작성하거나 메소드를 호출 할 수는 없습니다. 다시 말해 반사를 통해 볼 수는 있지만 액세스 할 수는 없습니다. 그러나 중첩 된 정적 클래스가 공용 인 경우 정적으로 액세스 할 수 없지만 (컴파일 중에) 리플렉션을 통해 기본적으로 액세스 할 수 있습니다.
제시 글 리크

72

질문에 대한 답변이 있지만 중첩 된 인터페이스를 사용해야하는 한 가지 좋은 이유는 함수가 해당 클래스와 직접 관련이있는 것입니다. 이에 대한 좋은 예는 Listener입니다. 당신이 클래스를 가지고 있다면 Foo당신이 그것을에서 이벤트를 수신 할 수있는 다른 클래스를 원, 당신은라는 인터페이스를 선언 할 수 FooListener괜찮습니다,하지만 아마 중첩 된 인터페이스를 선언하는 것이 더 명확하고 그 다른 클래스가 구현하는 것이다 Foo.Listener( 중첩 클래스 Foo.Event는 이것과 함께 나쁘지 않습니다).


11
일반적인 예는 java.util.Map.Entry(다른 인터페이스에 중첩 된 인터페이스)입니다.
Paŭlo Ebermann

4
나는 이것이 오래된 주제라는 것을 알고 있지만 외부 클래스가 자체 패키지에 있고 추가 인터페이스 (예 :) Map.Entry또는 클래스가 해당 패키지 내에있는 것을 선호합니다 . 수업 시간을 단축하고 요점을 지키기 때문에 이렇게 말합니다. 또한 독자는 패키지의 클래스를보고 클래스와 관련된 다른 엔티티를 볼 수 있습니다. java.collections.map지도 용 패키지가있을 것입니다 . 이것은 OO와 모듈성에 관한 것입니다. java.util그것에 너무 많이 있습니다. util같은 common냄새 IMO
David Kerr

1
@ 얽히고 설킨 : java.util확실히 너무 많이 있습니다. 즉, 제안 한대로 세밀한 패키지로 나누는 것이 이상적이라고 생각하지 않습니다.
ColinD

1
@DavidKerr이 인터페이스를 java.util.MapEntry자체 패키지 외부에서 호출한다고 가정하십시오 . 첫 번째 반사 :이 클래스와 관련된 인터페이스는 무엇입니까? 수업을 봅니다. javadoc이이 인터페이스에 링크하지 않는 한, 나는 그들의 관계에 대한 실마리가 없다. 또한 사람들은 가져 오기 패키지를 보지 않습니다. 모두 자신의 의견이 있습니다. 아무도 옳지 않다, 아무도 잘못하지 않았다
Raymond Chenon

@RaymondChenon 내가 말한 것의 일부는 Map과 관련 클래스 (예 : Map.Entry)를 별도의 패키지 (예 : java.collections.map)에 넣는 것이 었습니다. 이렇게하면지도와 관련된 모든 코드가 단일 위치 (패키지)에 있으며 최상위지도 클래스에 부담이 없습니다. 관련 구현 (HashMap 등)을 동일한 패키지에 넣었을 것입니다.
David Kerr

14

멤버 인터페이스는 암시 적으로 정적입니다. 예제의 정적 수정자는 코드의 의미를 변경하지 않고 제거 할 수 있습니다. Java 언어 사양 8.5.1 도 참조하십시오 . 정적 멤버 유형 선언


이것은 '내부 인터페이스'가 아니며 중첩 된 인터페이스입니다. 내부는 Java에서 특정 의미를 갖습니다.
Lorne의 후작

@ EJP, 나는 용어를 상호 교환 적으로 사용하여 다양한 블로그를 읽었습니다. 내부 인터페이스가 있습니까? 중첩 된 인터페이스와 다른 점은 무엇입니까?
Number945

@BreakingBenjamin, 중첩 인터페이스는 정적을 의미합니다. 내부 클래스와 중첩 클래스가 있지만 내부 인터페이스는 없습니다. 그렇더라도 중첩 된 인터페이스로 호출해야합니다. 내부-비 정적 및 중첩은 정적입니다.
Sundar Rajan

9

내부 인터페이스에 액세스하려면 정적이어야합니다. 인터페이스는 클래스의 인스턴스와 관련이 없지만 클래스 자체 Foo.Bar와 연결되므로 다음과 같이 액세스됩니다 .

public class Baz implements Foo.Bar {
   ...
}

대부분의 경우 이것은 정적 내부 클래스와 다르지 않습니다.


35
중첩 된 인터페이스는 키워드 작성 여부에 관계없이 자동으로 정적입니다.
Paŭlo Ebermann

3
우리는 커뮤니티가 다른 답변을 받아들이 기 위해 투표 할 수있는 방법이 정말로 필요합니다 : stackoverflow.com/a/74400/632951
Pacerier


@ ClintonN.Dreisbach The interface isn't associated with instances of the class, but with the class itself더 자세한 의미를 설명해 주실 수 는 없습니다.
Kasun Siyambalapitiya

6

Jesse의 대답은 가깝지만 내부 인터페이스가 유용한 이유를 보여주는 더 나은 코드가 있다고 생각합니다. 읽기 전에 아래 코드를 살펴보십시오. 내부 인터페이스가 유용한 이유를 찾을 수 있습니까? 대답은 내부 인터페이스를 사용하여 DoSomethingAlready 클래스를 인스턴스화 할 수 있다는 것 입니다. 사용자 정의 유형을 구성하고 캡슐화를 향상시킵니다 . 클래스를 A와 C를 구현하는 모든 클래스 있다는 것입니다. 콘크리트 클래스 동물원 만이 아닙니다. 물론 이것은 AC가 내부가 아니더라도 달성 할 수 있지만 A와 C뿐만 아니라 더 긴 이름을 연결하고 다른 조합 (예 : A 및 B, C 및 B 등)을 위해이 작업을 수행하는 것을 상상해보십시오. 상황이 어떻게 통제되지 않는지보십시오. 말할 것도없이 소스 트리를 검토하는 사람들은 한 클래스에서만 의미있는 인터페이스에 압도 될 것입니다.

class ConcreteA implements A {
 :
}

class ConcreteB implements B {
 :
}

class ConcreteC implements C {
 :
}

class Zoo implements A, C {
 :
}

class DoSomethingAlready {
  interface AC extends A, C { }

  private final AC ac;

  DoSomethingAlready(AC ac) {
    this.ac = ac;
  }
}

2
이것은 말도 안됩니다. 클래스 Zoo는 인터페이스를 구현 하지 않으므로AC 인스턴스가 예상 Zoo되는 생성자에 전달 될 수 없습니다 . 사실 모두를 확장 하고 , 클래스가 구현하는 것을 의미하지는 않습니다 및 마술도 구현합니다 . DoSomethingAlreadyACACACACAC
Holger

3

질문에 직접 대답하려면 Map.Entry를보십시오.

지도

또한 이것은 유용 할 수 있습니다

정적 중첩 인터페이스 블로그 항목


4
이것은 예이지만 실제로는 대답이 아닙니다.
Paŭlo Ebermann

Map.Entry를 사용하여 "쌍"개체를 많이 만듭니다. 노출되어 있습니다. 한 쌍의 구현에는 두 가지 생각의 학교가 있지만 여기서는 그렇지 않습니다. Map.Entry는 내부 일 수 있지만 외부에서 사용합니다.
Ravindranath Akila

0

일반적으로 정적 내부 클래스가 보입니다. 정적 내부 클래스는 비 정적 클래스가 할 수있는 포함 클래스를 참조 할 수 없습니다. 패키지 충돌이 발생하지 않는 한 (이미 Foo와 동일한 패키지에 Bar라는 인터페이스가 있습니다) 필자는 자체 파일로 만들 것이라고 생각합니다. Foo와 Bar 사이의 논리적 연결을 적용하기위한 설계 결정일 수도 있습니다. 아마도 저자는 Bar가 Foo에서만 사용되도록 의도했을 것입니다 (정적 내부 인터페이스는 이것을 논리적 연결로 강제하지는 않지만)


'정적 내부'는 용어의 모순입니다. 중첩 된 클래스들은 하나 정적 또는 내부.
Lorne의 후작 22

0

Foo 클래스를 Foo 인터페이스로 변경하면 위 예제의 "public"키워드도 중복됩니다.

다른 인터페이스 내에 정의 된 인터페이스는 암시 적으로 공개 정적입니다.


질문에 대답하지 않았습니다
Raining

0

1998 년 Philip Wadler는 정적 인터페이스와 비 정적 인터페이스의 차이점을 제안했습니다.

내가 볼 수있는 한, 인터페이스를 비 정적으로 만드는 것의 유일한 차이점은 이제 비 정적 내부 클래스를 포함 할 수 있다는 것입니다. 따라서 변경으로 인해 기존 Java 프로그램이 유효하지 않게됩니다.

예를 들어 그는 표현 문제에 대한 해결책을 제안했는데 , 이는 표현이 "언어로 표현할 수 있는가"라는 표현과 표현이 "언어로 표현하려고하는 용어"가 아닌 표현 사이의 불일치입니다. .

정적 및 비 정적 중첩 인터페이스의 차이점에 대한 예제는 샘플 코드 에서 확인할 수 있습니다 .

// This code does NOT compile
class LangF<This extends LangF<This>> {
    interface Visitor<R> {
        public R forNum(int n);
    }

    interface Exp {
        // since Exp is non-static, it can refer to the type bound to This
        public <R> R visit(This.Visitor<R> v);
    }
}

그의 제안은 결코 Java 1.5.0에서 이루어지지 않았습니다. 따라서 다른 모든 대답은 정확합니다. 정적 및 비 정적 중첩 인터페이스에는 차이가 없습니다.


이 모든 것은 Java의 제네릭을위한 매우 초기 프로세서 인 GJ를 나타냅니다.
Lorne의 후작

-1

Java에서 정적 인터페이스 / 클래스는 인터페이스 / 클래스를 최상위 클래스처럼 사용할 수 있습니다. 즉, 다른 클래스에서 선언 할 수 있습니다. 그래서 당신은 할 수 있습니다 :

class Bob
{
  void FuncA ()
  {
    Foo.Bar foobar;
  }
}

정적이 없으면 위의 컴파일에 실패합니다. 이것의 장점은 인터페이스를 선언하기 위해 새 소스 파일이 필요하지 않다는 것입니다. 또한 Foo.Bar를 작성해야하므로 인터페이스 Bar를 시각적으로 Foo 클래스에 시각적으로 연결하며 Foo 클래스가 Foo.Bar의 인스턴스로 무언가를 수행함을 의미합니다.

자바 클래스 유형에 대한 설명 .


정적 없이는 않습니다 컴파일. '정적'은 중복입니다.
Lorne의 후작 22

@ EJP : 내가 링크 한 기사에 따르면 정적없이 내부 클래스는 소유 클래스에서만 사용할 수 있으며 owing 클래스 외부에서는 사용할 수 없습니다. 정적은 클래스를 중첩 된 최상위 클래스로 만들고 소유 클래스의 범위를 벗어난 객체 (적어도 기사에 따르면)에서 사용할 수 있습니다. 정적은 전적으로 중복되지 않으며 내부 클래스의 범위에 영향을줍니다. 이것은 클래스에 적용되며, 인터페이스는 항상 최상위 객체처럼 작동 할 수 있습니다 (확인하려면 언어 사양을 읽어야 함). 아마도 내 대답의 인터페이스와 클래스 부분을 분리해야했을 것입니다 (나쁜).
Skizz

-6

정적은 패키지 (프로젝트)의 모든 클래스 부분이 포인터를 사용하지 않고 패키지에 액세스 할 수 있음을 의미합니다. 상황에 따라 유용하거나 방해가 될 수 있습니다.

"정적"메소드의 유용한 예는 Math 클래스입니다. Math의 모든 메소드는 정적입니다. 즉, 방해가되지 않고 새로운 인스턴스를 만들고 변수를 선언하고 더 많은 변수에 저장할 필요가 없습니다. 데이터를 입력하고 결과를 얻을 수 있습니다.

정적이 항상 유용한 것은 아닙니다. 예를 들어 대 / 소문자를 비교하는 경우 여러 가지 다른 방식으로 데이터를 저장할 수 있습니다. 동일한 서명으로 3 개의 정적 메서드를 만들 수 없습니다. 정적이 아닌 3 가지 인스턴스가 필요하며 정적 인 경우 입력과 함께 데이터가 변경되지 않으므로 원인과 비교할 수 있습니다.

정적 방법은 일회성 반환 및 빠른 계산 또는 쉽게 얻을 수있는 데이터에 적합합니다.


1
나는 정적 인 의미가 무엇인지 안다. 나는 전에 인터페이스에서 본 적이 없다. 그러므로 귀하의 질문은 주제가
맞지 않습니다

1
나는 그의 대답이 주제가 아닌 것 같아요.
Koray Tugay
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.