[java] 정적이 아닌 내부 클래스에 정적 메서드를 사용할 수없는 이유는 무엇입니까?

왜 정적이 아닌 내부 클래스에 정적 메소드를 가질 수 없습니까?

내부 클래스를 정적으로 만들면 작동합니다. 왜?



답변

내부 클래스의 인스턴스는 외부 클래스의 인스턴스와 암시 적으로 연결되므로 정적 메서드 자체를 정의 할 수 없습니다. 정적 중첩 클래스는 포함 클래스에 정의 된 인스턴스 변수 또는 메소드를 직접 참조 할 수 없으므로 객체 참조를 통해서만 사용할 수 있으므로 정적 중첩 클래스에서 정적 메소드를 선언하는 것이 안전합니다.


답변

정적이 아닌 내부 클래스에서 정적 메서드를 허용 할 필요는 없습니다. 어떻게 접근하겠습니까? 외부 클래스 인스턴스를 거치지 않으면 비 정적 내부 클래스 인스턴스에 액세스 할 수 없습니다 (최소한 초기에). 정적이 아닌 내부 클래스를 만드는 순수한 정적 방법은 없습니다.

외부 클래스의 Outer경우 다음 test()과 같은 정적 메소드에 액세스 할 수 있습니다 .

Outer.test();

정적 내부 클래스의 Inner경우 다음 innerTest()과 같이 정적 메소드에 액세스 할 수 있습니다 .

Outer.Inner.innerTest();

그러나 Inner정적이 아닌 경우 메소드를 참조하는 완전히 정적 인 방법은 없습니다 innertest. 비 정적 내부 클래스는 외부 클래스의 특정 인스턴스에 연결됩니다. Outer.Inner.CONSTANT함수 호출 Outer.Inner.staticFunction();이 아닌 방식으로에 대한 참조 가 명확하게 보장 된다는 점에서 함수는 상수와 다릅니다 . 에 정의 된 Inner.staticFunction()호출 이 있다고 가정 해 보겠습니다 . 정적 함수를 호출하려고하면 이제 Inner 클래스에 대한 모호한 참조가 생깁니다. 즉, 내부 클래스의 어느 인스턴스에서 정적 함수를 호출합니까? 그것은 중요. 외부 객체에 대한 암시 적 참조로 인해 정적 메소드를 참조하는 정적 방법은 없습니다.getState()Outer

Paul Bellora는 언어 디자이너가 이것을 허용했을 수도 있습니다. 그런 다음 정적이 아닌 내부 클래스의 정적 메서드에서 외부 클래스에 대한 암시 적 참조에 대한 액세스를 신중하게 허용하지 않아야합니다. 이때 정적 클래스를 제외하고 외부 클래스를 참조 할 수없는 경우 내부 클래스가되는 값은 무엇입니까? 그리고 정적 액세스가 괜찮다면 왜 내부 클래스 전체를 정적으로 선언하지 않습니까? 내부 클래스 자체를 정적으로 만들면 외부 클래스에 대한 암시 적 참조가 없으며 더 이상이 모호성이 없습니다.

비 정적 내부 클래스에 실제로 정적 메소드 가 필요한 경우 설계를 다시 생각해야합니다.


답변

나는 틀릴 수도 있고 틀릴 수도있는 이론을 가지고있다.

먼저 내부 클래스가 Java로 구현되는 방법에 대해 알아야합니다. 이 수업이 있다고 가정 해보십시오.

class Outer {
    private int foo = 0;
    class Inner implements Runnable {
        public void run(){ foo++; }
    }
    public Runnable newFooIncrementer(){ return new Inner(); }
}

컴파일하면 생성 된 바이트 코드는 다음과 같이 쓴 것처럼 보입니다.

class Outer {
    private int foo = 0;
    static class Inner implements Runnable {
        private final Outer this$0;
        public Inner(Outer outer){
            this$0 = outer;
        }
        public void run(){ this$0.foo++; }
    }
    public Runnable newFooIncrementer(){ return new Inner(this); }
}

비 정적 내부 클래스에서 정적 메서드를 허용 한 경우 이와 같은 작업을 수행 할 수 있습니다.

class Outer {
    private int foo = 0;
    class Inner {
        public static void incrFoo(){ foo++; }
    }
}

Inner클래스 당 하나의 화신이 있는 것처럼 보이므로 상당히 합리적 Outer입니다. 그러나 위에서 본 것처럼 정적이 아닌 내부 클래스는 실제로 정적 “내부”클래스의 구문 설탕이므로 마지막 예제는 대략 다음과 같습니다.

class Outer {
    private int foo = 0;
    static class Inner {
        private final Outer this$0;
        public Inner(Outer outer){
            this$0 = outer;
        }
        public static void incrFoo(){ this$0.foo++; }
    }
}

this$0정적 이기 때문에 분명히 작동하지 않습니다 . 이런 종류의 정적 메소드가 허용되지 않는 이유는 무엇입니까? 다른 객체의 정적이 아닌 내부 클래스의 인스턴스가 “정적 상태”를 공유하는 경우 반 직관적입니다. 또한 엔 클로징 오브젝트를 참조하지 않는 한 최종 필드 허용되는 이유도 설명합니다 .


답변

유일한 이유는 “필수적이지 않다”는 것인데, 왜 그것을지지해야 하는가?

구문 적으로 내부 클래스에 정적 멤버가있는 것을 금지 할 이유가 없습니다. 의 인스턴스가의 인스턴스와 Inner연결되어 있지만 정적 멤버를 참조하는 데 Outer여전히 사용할 수 있습니다.Outer.Inner.myStaticInner java가 그렇게하기로 결정한 경우 .

의 모든 인스턴스간에 무언가를 공유해야하는 경우 Inner이를 Outer정적 멤버 로 넣을 수 있습니다 . 에 이것은 당신이 정적 멤버를 사용하는 것보다하지 더 나쁜 Inner경우, Outer여전히의 private 멤버에 액세스 할 수 있습니다Inner (캡슐이 향상되지 않습니다) 어쨌든.

Inner하나의 outer객체 로 생성 된 모든 인스턴스간에 무언가를 공유 해야하는 경우 인스턴스에 넣는 것이 더 합리적입니다.Outer 일반 구성원 클래스 입니다.

“정적 중첩 클래스는 거의 최상위 클래스”라는 의견에 동의하지 않습니다. 정적 중첩 클래스 / 내부 클래스는 외부 클래스의 일부로 간주하는 것이 좋습니다. 외부 클래스의 개인 멤버에 액세스 할 수 있기 때문입니다. 그리고 외부 계급의 구성원도 “내부 계급의 구성원”입니다. 따라서 내부 클래스에서 정적 멤버를 지원할 필요가 없습니다. 외부 클래스의 일반 / 정적 구성원이면 충분합니다.


답변

보낸 사람 : https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html

인스턴스 메서드 및 변수와 마찬가지로 내부 클래스는 해당 클래스의 인스턴스와 연결되며 해당 개체의 메서드 및 필드에 직접 액세스 할 수 있습니다. 또한 내부 클래스는 인스턴스와 연결되므로 정적 멤버 자체를 정의 할 수 없습니다.

오라클의 설명은 피상적이고 손으로 만듭니다. 내부 클래스 내에서 정적 멤버를 선점해야 할 기술적 또는 구문상의 이유가 없기 때문에 (C #과 같은 다른 언어로 허용됨) Java 디자이너의 동기는 개념적 취향 및 / 또는 기술적 편의성 문제 일 수 있습니다.

내 추측은 다음과 같습니다.

최상위 클래스와 달리 내부 클래스는 인스턴스에 따라 다릅니다. 내부 클래스 인스턴스는 모든 외부 클래스의 인스턴스와 연결되며 해당 멤버에 직접 액세스 할 수 있습니다. 이것이 자바를 사용하게하는 주요 동기입니다. 다른 방법으로 표현 : 내부 클래스가되는 의미 외부 클래스 인스턴스의 맥락에서 인스턴스화. 외부 클래스 인스턴스가 없으면 내부 클래스는 외부 클래스 의 다른 인스턴스 멤버보다 더 이상 사용할 수 없습니다 . 이것을 내부 클래스 의 인스턴스 종속 정신 이라고하겠습니다 .

정적 멤버 (객체 지향이 아님)의 본질은 외부 클래스 인스턴스없이 내부 클래스의 정적 멤버를 참조 / 호출 할 수 있기 때문에 인스턴스 지향적 인 내부 클래스 정신 ( 객체 지향)과 충돌 합니다. 규정 된 내부 클래스 이름 사용

정적 변수는 특히 또 다른 방식으로 기분을 상하게 할 수 있습니다. 외부 클래스의 다른 인스턴스와 연관된 내부 클래스의 두 인스턴스는 정적 변수를 공유합니다. 변수는 상태의 구성 요소이므로 사실상 두 개의 내부 클래스 인스턴스는 연결된 외부 클래스 인스턴스와 독립적으로 상태를 공유합니다. 정적 변수가 이런 식으로 작동하는 것은 용납 할 수없는 것은 아니지만 (Java에서 OOP 순도에 대한 합리적인 타협으로 허용 함) 인스턴스가 이미 외부 클래스 인스턴스와 결합 된 내부 클래스에서 허용함으로써 더 큰 위반이있을 수 있습니다. 디자인에 의해. 인스턴스 종속 정신 에 찬성하여 내부 클래스 내에서 정적 멤버를 금지 하면이 더 깊은 OOP 공격을 선점하는 추가 보너스가 제공됩니다.

다른 한편으로, 그러한 위반은 정적 상수에 의해 수반되지 않으며, 이는 의미 상 상태를 구성하지 않으므로 허용 될 수 있습니다. 인스턴스 종속 정신과 의 최대 일관성을 위해 정적 상수를 금지하지 않는 이유 입니까? 아마도 상수 는 필요한 것보다 더 많은 메모리를 차지할 필요가 없기 때문에 (정적이지 않으면 강제로 낭비되는 모든 내부 클래스 인스턴스에 복사됩니다) 그렇지 않으면 예외의 이유를 상상할 수 없습니다.

확실한 추론은 아니지만 IMO는 그 문제에 대한 오라클의 커서 발언을 가장 잘 이해합니다.


답변

짧은 대답 : 대부분의 프로그래머가 범위가 작동하는 방식에 대한 정신 모델은 javac에서 사용하는 모델이 아닙니다. 보다 직관적 인 모델을 찾으려면 javac 작동 방식에 큰 변화가 필요했을 것입니다.

내부 클래스의 정적 멤버가 바람직한 주된 이유는 코드 청결도 때문입니다. 내부 클래스에서만 사용되는 정적 멤버는 외부 클래스에 배치하지 않고 내부 클래스에 있어야합니다. 치다:

class Outer {
   int outID;

   class Inner {
      static int nextID;
      int id = nextID++;

      String getID() {
         return outID + ":" + id;
      }
   }
}

규정되지 않은 식별자 “outID”를 사용할 때 getID ()에서 무슨 일이 일어나고 있는지 고려하십시오. 이 식별자가 나타나는 범위는 다음과 같습니다.

Outer -> Inner -> getID()

여기서도 javac가 작동하는 방식이므로 범위의 “Outer”레벨에는 Outer의 정적 및 인스턴스 멤버가 모두 포함됩니다. 우리는 일반적으로 클래스의 정적 부분을 다른 수준의 범위로 생각해야하므로 혼동됩니다.

Outer static -> Outer instance -> instanceMethod()
         \----> staticMethod()

물론 이런 생각으로 staticMethod ()는 외부의 정적 멤버 만 볼 수 있습니다. 그러나 이것이 javac가 작동하는 방식이라면 정적 메소드에서 인스턴스 변수를 참조하면 “이름을 확인할 수 없습니다”오류가 발생합니다. 실제로 발생하는 것은 이름이 범위에 있지만 추가 수준의 확인이 시작되어 이름이 인스턴스 컨텍스트에서 선언되었고 정적 컨텍스트에서 참조되고 있음을 알아내는 것입니다.

자, 이것이 내부 클래스와 어떤 관련이 있습니까? 우리는 내부 클래스가 정적 범위를 가질 수없는 이유가 없다고 생각합니다.

Outer static -> Outer instance -> Inner instance -> getID()
         \------ Inner static ------^

즉, 내부 클래스의 정적 선언과 외부 클래스의 인스턴스 선언은 모두 내부 클래스의 인스턴스 컨텍스트 내에 있지만 실제로는 다른 클래스에 중첩되지 않습니다. 둘 다 외부의 정적 범위에 중첩됩니다.

그것은 javac가 작동하는 방식이 아닙니다. 정적 및 인스턴스 멤버 모두에 대한 단일 레벨 범위가 있으며 범위는 항상 엄격하게 중첩됩니다. 상속은 심지어 수퍼 클래스 범위를 분기하고 검색하는 대신 선언을 서브 클래스에 복사하여 구현됩니다.

중 분할 정적을 가지고 예를 스코프 것 javac의 내부 클래스의 정적 멤버 지원 지원 분기 및 범위 계층 구조를 재결합, 또는이 모든 수준에서 컨텍스트의 유형을 추적하기 위해 변화에 간단한 부울 “정적 컨텍스트”개념을 확장 할 것을 현재 범위의 중첩 클래스


답변

왜 정적이 아닌 내부 클래스에 정적 메소드를 가질 수 없습니까?

참고 : 정적이 아닌 중첩 클래스는 내부 클래스로 알려져 있으므로 그렇게하지 마십시오 non-static inner class.

내부 클래스 인스턴스는 해당 외부 클래스 인스턴스가 없으면 존재하지 않습니다. 내부 클래스는 컴파일 시간 상수 이외의 정적 멤버를 선언 할 수 없습니다. 그것이 허용된다면의 의미에 대한 모호성이 있었을 것이다 static. 이 경우 특정 혼란이 있었을 것입니다.

  1. VM에 인스턴스가 하나만 있다는 의미입니까?
  2. 아니면 외부 객체 당 하나의 인스턴스입니까?

그렇기 때문에 설계자들은 아마도이 문제를 전혀 처리하지 않기로 결정한 것입니다.

내부 클래스를 정적으로 만들면 작동합니다. 왜 ?

다시 내부 클래스를 정적으로 만들 수 없으며 정적 클래스를 중첩으로 선언 할 수 있습니다. 이 경우이 중첩 클래스는 실제로 외부 클래스의 일부이며 문제없이 정적 멤버를 가질 수 있습니다.