Java가 내부 클래스에서 정적 필드를 금지하는 이유는 무엇입니까?


85
class OuterClass {
 class InnerClass {
  static int i = 100; // compile error
  static void f() { } // compile error
 }
} 

를 사용하여 정적 필드에 액세스 할 OuterClass.InnerClass.i수는 없지만 정적이어야하는 항목 (예 : 생성 된 InnerClass 개체 수)을 기록하려면 해당 필드를 정적으로 만드는 것이 도움이됩니다. 그렇다면 Java는 내부 클래스에서 정적 필드 / 메서드를 금지합니까?

편집 : 정적 중첩 클래스 (또는 정적 내부 클래스)로 컴파일러를 행복하게 만드는 방법을 알고 있지만 Java가 언어 디자인과 내부 클래스 내부의 정적 필드 / 메서드를 금지하는 이유를 알고 싶습니다. 누군가가 그것에 대해 더 많이 알고 있다면 구현 측면.


3
제가 가장 좋아하는 예는 내부 클래스를위한 로거입니다. 다른 모든 로거처럼 정적 일 수는 없습니다.
Piotr Findeisen 2013

답변:


32

내부 클래스의 개념은 둘러싸는 인스턴스의 컨텍스트에서 작동하는 것입니다. 어떻게 든 정적 변수와 메서드를 허용하는 것이 이러한 동기와 모순됩니까?

8.1.2 내부 클래스 및 엔 클로징 인스턴스

내부 클래스는 명시 적으로 또는 암시 적으로 정적으로 선언되지 않은 중첩 클래스입니다. 내부 클래스는 정적 이니셜 라이저 (§8.7) 또는 멤버 인터페이스를 선언 할 수 없습니다. 내부 클래스는 컴파일 타임 상수 필드 (§15.28)가 아닌 경우 정적 멤버를 선언 할 수 없습니다.


18
어쩌면 그냥있어 결정 처럼
그레고리 Pakosz에게

3
당신은 할 수 인스턴스를 부모 관계없이 비 정적 내부를,하지만 당신은 여전히 수 초기화 를.
skaffman

ClassLoaders가 "Class X가 초기화되었습니다"라는 캐시를 유지하는 경우 해당 논리는 Class [객체가 나타내는] X의 여러 인스턴스를 초기화하는 데 사용할 수 없습니다 (이는 Class 객체를 여러 내부 클래스로 인스턴스화해야 할 때 필요합니다. 별개의 개체).
Erwin Smout 2015 년

@skaffman 여전히 말이되지 않습니다. 내부 클래스의 정적 속성은 한 번만 초기화되므로 문제가 무엇입니까? 지금은 정적 해시 맵이 있고이 맵만 조작하는 약 4 개의 메서드가 있으므로 모든 것을 내부 클래스로 그룹화하는 것이 더 이상적입니다. 그러나 정적 해시 맵은 이제 외부에 있어야하며 다른 관련 항목이있을 수 있습니다. 이는 어리석은 일입니다. 정적 속성을 초기화하면 어떤 문제가 있습니까?
mmm

54

내가 알고 싶은 것은 자바가 내부 클래스 내부의 정적 필드 / 메서드를 금지하는 이유입니다.

내부 클래스는 "인스턴스"내부 클래스이기 때문입니다. 즉, 둘러싸는 개체의 인스턴스 속성과 같습니다.

그들은 "인스턴스"클래스이기 때문에 static기능 을 허용 하는 static것은 의미가 없습니다. 왜냐하면는 처음에 인스턴스없이 작동하기 위한 것 입니다.

정적 / 인스턴스 속성을 동시에 생성하려고하는 것과 같습니다.

다음 예를 살펴보십시오.

class Employee {
    public String name;
}

두 개의 employee 인스턴스를 생성하는 경우 :

Employee a = new Employee(); 
a.name = "Oscar";

Employee b = new Employee();
b.name = "jcyang";

왜 각각의 자산에 대해 고유 한 가치가 있는지 분명합니다. name그렇죠?

내부 클래스도 마찬가지입니다. 각 내부 클래스 인스턴스는 다른 내부 클래스 인스턴스와 독립적입니다.

따라서 counter클래스 속성 을 만들려고하면 두 개의 다른 인스턴스에서 해당 값을 공유 할 방법이 없습니다.

class Employee {
    public String name;
    class InnerData {
        static count; // ??? count of which ? a or b? 
     }
}

인스턴스 생성 할 때 ab위의 예를, 어떤 정적 변수에 대한 올바른 값이 될 것이다 count? InnerData클래스 의 존재 는 둘러싸고있는 각 객체에 완전히 의존 하기 때문에이를 결정할 수 없습니다 .

그렇기 때문에 클래스가으로 선언되면 static더 이상 살아있는 인스턴스가 필요하지 않습니다. 이제 종속성이 없으므로 정적 속성을 자유롭게 선언 할 수 있습니다.

반복적으로 들리지만 인스턴스 속성과 클래스 속성의 차이점을 생각하면 이해가 될 것입니다.


4
내부 클래스의 정적 속성 에 대한 설명을 구입할 수 있지만 @skaffman이 내 대답에 대한 주석에서 지적했듯이 정적 메서드는 어떻습니까? 메서드가 인스턴스에서 분리되어야하는 의무없이 허용되어야하는 것처럼 보입니다. 실제로 자바에서는 인스턴스에 대한 정적 메서드를 호출 할 수 있습니다 (나쁜 스타일로 간주되지만). BTW : 나는 C #과 영업 이익의 코드를 컴파일을 시도하는 동료를 물었다는 않습니다 컴파일. 따라서 C #은 OP가 원하는 것이 근본적인 OO 원칙을 위반하지 않는다는 것을 보여주는 것을 분명히 허용합니다.
Asaph

2
방법과 똑같습니다. 여기서 요점은 속성 이나 메서드 가 statis인지 아닌지가 아니라 내부 클래스 자체가 인스턴스 "stuff"라는 사실입니다. 내 말은, 여기서 문제는 외부 클래스가 생성 될 때까지 그러한 내부 클래스의 인스턴스가 존재하지 않는다는 것입니다. 따라서 아무것도 없으면 어떤 메서드가 발송됩니다. 처음에 인스턴스가 필요하기 때문에 공기를 쳤다.
OscarRyz

1
C #에 대해 ... 음. C #에서 허용한다고해서 OO가 유효하지 않습니다. 틀린 것은 아니지만 C #에는 일관성을 희생하더라도 개발을 더 쉽게 할 수있는 몇 가지 패러다임이 포함되어 있습니다 (각 .NET 릴리스마다 새로운 것을 배워야 함). 이것과 다른 종류의 것들을 허용합니다. 좋은 일이라고 생각합니다. 커뮤니티가 추가 기능이 충분히 멋지다고 느끼면 C #이 향후에 사용할 수 있습니다.
OscarRyz

2
@OscarRyz 정적 메서드 / 필드 를 사용하기 위해 내부 클래스의 인스턴스가 필요한 이유는 무엇 입니까? 내부 클래스의 [유용한] 정적 메서드의 예 개인 도우미 메서드입니다.
Leonid Semyonov

1
를 사용하면 finaljava의 내부 클래스에서 정적 필드가 허용됩니다. 이 시나리오를 어떻게 설명합니까?
Number945

34

InnerClassstatic인스턴스 (의 OuterClass)에 속하므로 구성원을 가질 수 없습니다 . 당신이 선언하면 InnerClass같은 static인스턴스에서 분리하기 위해 코드를 컴파일합니다.

class OuterClass {
    static class InnerClass {
        static int i = 100; // no compile error
        static void f() { } // no compile error
    }
}

BTW : .NET Framework의 인스턴스를 계속 만들 수 있습니다 InnerClass. static이 컨텍스트에서 OuterClass.


6
InnerClass않습니다 하지 에 속하는 OuterClass, 인스턴스 그것의는 않습니다. 두 클래스 자체에는 그러한 관계가 없습니다. 왜 당신이 정적 메소드를 가질 수 없는지에 대한 질문은 InnerClass여전히 존재합니다.
skaffman

9

실제로 정적 필드가 상수이고 컴파일 타임에 기록 된 경우 선언 할 수 있습니다.

class OuterClass {
    void foo() {
        class Inner{
            static final int a = 5; // fine
            static final String s = "hello"; // fine
            static final Object o = new Object(); // compile error, because cannot be written during compilation
        }
    }
}

8
  1. 클래스 초기화 순서 는 중요한 이유입니다.

내부 클래스는 엔 클로징 / 외부 클래스의 인스턴스에 종속되므로 Inner 클래스를 초기화하기 전에 Outer 클래스를 초기화해야합니다.
이것은 JLS가 클래스 초기화에 대해 말합니다. 우리가 필요로하는 요점은 다음과 같은 경우 클래스 T가 초기화된다는 것입니다.

  • T에 의해 선언 된 정적 필드가 사용되며 필드는 상수 변수가 아닙니다.

따라서 내부 클래스에 액세스하는 정적 필드가 있으면 내부 클래스가 초기화되지만 포함하는 클래스가 초기화되지는 않습니다.

  1. 그것은 몇 가지 기본 규칙을 위반합니다 . 멍청한 물건을 피하기 위해 마지막 섹션 (까지 two cases)으로 건너 뛸 수 있습니다.

에 대한 한 가지 , 일부 는 모든면에서 일반 클래스처럼 작동하고 Outer 클래스와 연결된다는 것입니다.static nested classnested classstatic

그러나 Inner class/ 의 개념은 외부 / 인 클로징 클래스 와 연관 될 것 입니다. 클래스가 아닌 인스턴스 와 연관되어 있습니다. 이제 인스턴스와 연결한다는 것은 인스턴스 변수의 개념에서 볼 때 인스턴스 내부에 존재하며 인스턴스간에 달라짐을 의미합니다. non-static nested classinstance

이제 무언가를 정적으로 만들면 클래스가로드 될 때 초기화되고 모든 인스턴스간에 공유되어야합니다. 그러나 비정 적이기 때문에 내부 클래스 자체 ( 지금은 내부 클래스의 인스턴스에 대해 확실히 잊을 수 있음 )가 외부 / 인 클로징 클래스의 모든 인스턴스 ( 적어도 개념적으로 ) 와 공유되지 않습니다 . 그러면 어떻게 일부 변수를 기대할 수 있습니까? 내부 클래스의 모든 인스턴스간에 공유됩니다.

따라서 Java에서 정적 중첩 클래스가 아닌 정적 변수를 사용할 수 있다면. 두 가지 경우 가 있습니다 .

  • 내부 클래스의 모든 인스턴스와 공유하면 context of instance(인스턴스 변수) 개념을 위반하게됩니다 . 그럼 아니오입니다.
  • 모든 인스턴스와 공유되지 않으면 정적 개념을 위반하게됩니다. 다시 NO.

5

이 "한계"에 가장 적합한 동기는 다음과 같습니다. 내부 클래스의 정적 필드 동작을 외부 개체의 인스턴스 필드로 구현할 수 있습니다. 따라서 정적 필드 / 메서드 가 필요하지 않습니다 . 내가 의미하는 동작은 일부 개체의 모든 내부 클래스 인스턴스가 필드 (또는 메서드)를 공유한다는 것입니다.

따라서 모든 내부 클래스 인스턴스를 세고 싶다고 가정하면 다음과 같이 할 수 있습니다.

public class Outer{
    int nofInner; //this will count the inner class 
                  //instances of this (Outer)object
                  //(you know, they "belong" to an object)
    static int totalNofInner; //this will count all 
                              //inner class instances of all Outer objects
    class Inner {
        public Inner(){
            nofInner++;
            totalNofInner++;
        }
    }
}

2
그러나 문제는 정적 필드가 선언 될 때 허용하는 이유가 무엇 final입니까?
Solace

[ stackoverflow.com/a/1954119/1532220] (위의 OscarRyzs 답변) 을 보면 : 그의 동기는 값이 변수에 연결될 수 없다는 것입니다. 물론 변수가 최종적인 경우 할당 할 값을 매우 쉽게 알 수 있습니다 (알아야합니다).
ianos

2

간단히 말해서 비 정적 내부 클래스는 외부 클래스에 대한 인스턴스 변수로 외부 클래스가 생성되고 런타임에 외부 클래스 객체가 생성되고 클래스 로딩시 정적 변수가 생성 될 때만 생성됩니다. 따라서 비 정적 내부 클래스는 런타임 문제이므로 비 정적 내부 클래스의 일부가 아닌 정적입니다.

참고 : 내부 클래스는 항상 외부 클래스의 변수처럼 취급해야합니다. 다른 변수처럼 정적이거나 비정적일 수 있습니다.


그러나 내부 클래스는 static final상수 를 가질 수 있습니다 .
Raining


1

"정적"의 의미가 모호하기 때문입니다.

내부 클래스는 컴파일 타임 상수 이외의 정적 멤버를 선언 할 수 없습니다. "정적"의 의미에 대한 모호성이 있습니다. 가상 머신에 인스턴스가 하나만 있다는 의미입니까? 아니면 외부 개체 당 하나의 인스턴스 만? 언어 디자이너들은이 문제를 다루지 않기로 결정했습니다.

Cay S. Horstmann의 "초급자를위한 Core Java SE 9"에서 발췌. 90 페이지 2.6.3 장


-1

일관성을위한 것 같아요. 기술적 인 제한이없는 것 같지만 외부에서 내부 클래스의 정적 멤버에 액세스 할 수 없습니다. 즉 OuterClass.InnerClass.i, 중간 단계가 정적이 아니기 때문입니다.


그러나 내부 클래스는 static final상수 를 가질 수 있습니다 .
Raining
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.