많은 사람들이 Java 내부 클래스에 익숙하지 않을 수 있습니다. 실제로 C++에도 비슷한 개념이 존재합니다. 즉, 중첩 클래스 사이의 차이점과 연관성을 아래에서 비교하겠습니다. 표면적으로 내부 클래스는 클래스 내에 정의된 또 다른 클래스일 뿐이지만(아래에서 볼 수 있듯이 내부 클래스는 여러 위치에서 정의될 수 있음) 실제로는 그렇게 간단하지 않습니다. 초보자에게는 유용성이 그다지 명확하지 않을 수 있지만 이에 대해 더 깊이 이해하면 Java 디자이너가 내부 클래스에 좋은 의도를 가지고 있다는 것을 알게 될 것입니다. 내부 클래스 사용 방법을 배우는 것은 고급 Java 프로그래밍을 마스터하는 것의 일부이며 이를 통해 프로그램 구조를 보다 우아하게 디자인할 수 있습니다. 다음과 같은 측면에서 소개하겠습니다.
생면
다음과 같이 코드 코드를 복사합니다 .
공개 인터페이스 목차 {
정수값();
}
공용 인터페이스 대상 {
문자열 readLabel();
}
공개 클래스 상품 {
비공개 클래스 콘텐츠는 콘텐츠를 구현합니다.
개인 int i = 11;
공개 정수 값() {
내가 반환;
}
}
보호 클래스 GDestination은 Destination {을 구현합니다.
개인 문자열 라벨;
개인 GDestination(문자열 whereTo) {
라벨 = 어디에;
}
공개 문자열 readLabel() {
반품 라벨;
}
}
공개 목적지 dest(String s) {
새로운 GDestination을 반환합니다.
}
공개 콘텐츠 cont() {
새로운 콘텐츠()를 반환합니다.
}
}
클래스 TestGoods {
공개 정적 무효 메인(String[] args) {
상품 p = 새 상품();
내용 c = p.cont();
목적지 d = p.dest("베이징");
}
}
이 예에서 Content 및 GDestination 클래스는 Goods 클래스 내에 정의되어 있으며 액세스 수준을 제어하기 위해 각각 protected 및 private 한정자를 가집니다. Content는 상품의 내용을 나타내고, GDestination은 상품의 도착지를 나타냅니다. 그들은 각각 두 개의 인터페이스 Content와 Destination을 구현합니다. 다음 기본 메소드에서는 Contents c와 Destination d를 직접 사용하여 이 두 내부 클래스의 이름도 표시하지 않습니다. 이런 식으로 내부 클래스의 첫 번째 이점은 다른 사람이 알기를 원하지 않는 작업, 즉 캡슐화를 숨기는 것입니다.
동시에 우리는 외부 클래스의 범위 밖에서 내부 클래스 객체를 얻는 첫 번째 방법도 발견했습니다. 즉, 외부 클래스의 메서드를 사용하여 객체를 생성하고 반환하는 것입니다. 위 예제의 cont() 및 dest() 메서드가 이를 수행합니다. 그럼 다른 방법은 없나요?
물론 있습니다.
구문 형식은 다음과 같습니다.
externalObject=new externalClass(생성자 매개변수);
externalClass.innerClass innerObject=outerObject.new InnerClass(생성자 매개변수);
비정적 내부 클래스 객체를 생성할 때 먼저 해당 외부 클래스 객체를 생성해야 합니다. 그 이유는 다음 주제로 이어집니다. 비정적 내부 클래스 객체에는 외부 클래스 객체에 대한 참조가 있습니다.
이전 예제를 약간 수정합니다.
다음과 같이 코드 코드를 복사합니다 .
공개 클래스 상품 {
개인 int valueRate = 2;
비공개 클래스 콘텐츠는 콘텐츠를 구현합니다.
개인 int i = 11 * valueRate;
공개 정수 값() {
내가 반환;
}
}
보호 클래스 GDestination은 Destination {을 구현합니다.
개인 문자열 라벨;
개인 GDestination(문자열 whereTo) {
라벨 = 어디에;
}
공개 문자열 readLabel() {
반품 라벨;
}
}
공개 목적지 dest(String s) {
새로운 GDestination을 반환합니다.
}
공개 콘텐츠 cont() {
새로운 콘텐츠()를 반환합니다.
}
}
여기에서는 내부 클래스 Content의 value() 메소드가 값을 계산할 때 상품의 가치 계수를 곱하는 전용 멤버 변수 valueRate를 Goods 클래스에 추가합니다. 우리는 value()가 내부 클래스의 두 번째 이점인 valueRate에 액세스할 수 있다는 것을 발견했습니다. 내부 클래스 객체는 자신을 생성한 외부 클래스 객체의 내용, 심지어 개인 변수에도 액세스할 수 있습니다. 이는 디자인할 때 더 많은 아이디어와 지름길을 제공하는 매우 유용한 기능입니다. 이 기능을 수행하려면 내부 클래스 개체에 외부 클래스 개체에 대한 참조가 있어야 합니다. Java 컴파일러는 내부 클래스 객체를 생성할 때 외부 클래스 객체에 대한 참조를 암시적으로 전달하고 이를 유지합니다. 이를 통해 내부 클래스 개체는 항상 외부 클래스 개체에 액세스할 수 있으며, 이것이 외부 클래스 범위 외부에 내부 클래스 개체를 생성하려면 먼저 외부 클래스 개체를 생성해야 하는 이유이기도 합니다.
어떤 사람들은 내부 클래스의 멤버 변수가 외부 클래스의 멤버 변수와 동일한 이름을 갖는 경우, 즉 동일한 이름을 가진 외부 클래스의 멤버 변수가 차단되면 어떻게 해야 합니까? 괜찮습니다. Java는 외부 클래스에 대한 참조를 표현하기 위해 다음 형식을 사용합니다.
외부클래스.이것
그것으로 우리는 이러한 차폐 상황을 두려워하지 않습니다.
정적 내부 클래스
일반 클래스와 마찬가지로 내부 클래스도 정적일 수 있습니다. 그러나 비정적 내부 클래스와 비교하면 정적 내부 클래스에는 외부 참조가 없다는 차이점이 있습니다. 이는 실제로 C++의 중첩 클래스와 매우 유사합니다. Java 내부 클래스와 C++ 중첩 클래스의 가장 큰 차이점은 물론 외부에 대한 참조가 있는지 여부와 세부 사항에 차이가 있습니다.
또한 비정적 내부 클래스에는 정적 데이터, 정적 메서드 또는 다른 정적 내부 클래스가 있을 수 없습니다(내부 클래스는 두 수준 이상 중첩될 수 있음). 하지만 정적 내부 클래스에서는 이 모든 것을 가질 수 있습니다. 이것이 둘 사이의 두 번째 차이점이라고 볼 수 있습니다.
로컬 내부 클래스
예, Java 내부 클래스는 로컬일 수도 있고 메서드나 코드 블록 내에서 정의할 수도 있습니다.
다음과 같이 코드 코드를 복사합니다 .
공개 클래스 상품1 {
공개 목적지 dest(String s) {
GDestination 클래스는 Destination {을 구현합니다.
개인 문자열 라벨;
개인 GDestination(문자열 whereTo) {
라벨 = 어디에;
}
공개 문자열 readLabel() {
반품 라벨;
}
}
새로운 GDestination을 반환합니다.
}
공개 정적 무효 메인(String[] args) {
Goods1 g = 새로운 Goods1();
목적지 d = g.dest("베이징");
}
}
위의 내용은 그러한 예 중 하나입니다. dest 메소드에서는 내부 클래스를 정의하고, 마지막으로 이 메소드는 이 내부 클래스의 객체를 반환합니다. 내부 클래스의 객체를 생성하고 외부에 객체를 생성하기만 하면 이렇게 할 수 있습니다. 물론, 메소드에 정의된 내부 클래스는 디자인을 다양화할 수 있으며, 용도가 이에 국한되지는 않습니다.
더 이상한 예는 다음과 같습니다.
다음과 같이 코드 코드를 복사합니다 .
공개 클래스 상품2 {
개인 무효 내부 추적(부울 b) {
만약 (b) {
클래스 TrackingSlip {
개인 문자열 ID;
TrackingSlip(문자열 s) {
아이디 = s;
}
문자열 getSlip() {
반환 ID;
}
}
TrackingSlip ts = new TrackingSlip("슬립");
문자열 s = ts.getSlip();
}
}
공개 무효 트랙() {
내부 추적(true);
}
공개 정적 무효 메인(String[] args) {
Goods2 g = 새로운 Goods2();
g.트랙();
}
}
이 내부 클래스의 객체는 해당 범위를 벗어나므로 if 외부에서 생성할 수 없습니다. 그러나 컴파일하는 동안 내부 클래스 TrackingSlip은 자체 범위가 있고 이 범위를 벗어나면 유효하지 않다는 점을 제외하고 다른 클래스와 동시에 컴파일됩니다.
익명의 내부 클래스
Java의 익명 내부 클래스의 구문 규칙은 약간 이상해 보일 수 있지만 익명 배열과 마찬가지로 클래스의 객체만 생성하고 해당 이름이 필요하지 않은 경우 내부 클래스를 사용하면 코드가 간결하고 명확해 보일 수 있습니다. 구문 규칙은 다음과 같습니다.
새로운 인터페이스 이름(){......}; 또는 새로운 슈퍼클래스 이름(){......};
아래 예를 계속 진행해 보겠습니다.
다음과 같이 코드 코드를 복사합니다 .
공개 클래스 상품3 {
공개 콘텐츠 cont() {
새로운 콘텐츠 반환() {
개인 int i = 11;
공개 정수 값() {
내가 반환;
}
};
}
}
여기서 cont() 메소드는 익명의 내부 클래스를 사용하여 Contents 인터페이스를 구현하는 클래스의 객체를 직접 반환하는데, 이는 실제로 매우 간단해 보입니다.
Java 이벤트 처리를 위한 익명 어댑터에서는 익명 내부 클래스가 널리 사용됩니다. 예를 들어 창을 닫으려면 다음 코드를 추가하세요.
다음과 같이 코드 코드를 복사합니다 .
프레임.addWindowListener(새 WindowAdapter(){
공공 무효 windowClosing(WindowEvent e){
시스템.exit(0);
}
});
한 가지 주목할 점은 익명 내부 클래스에는 이름이 없으므로 생성자가 없다는 것입니다(그러나 익명 내부 클래스가 매개변수화된 생성자만 포함하는 상위 클래스를 상속하는 경우 생성 시 이러한 매개변수를 가져와야 하며 super 구현 과정에서 해당 콘텐츠를 호출하는 키워드). 멤버 변수를 초기화하려면 다음과 같은 여러 가지 방법이 있습니다.
메서드의 익명 내부 클래스에 있는 경우 이 메서드를 사용하여 원하는 매개변수를 전달할 수 있지만 이러한 매개변수는 최종으로 선언되어야 한다는 점을 기억하세요.
생성자를 가질 수 있도록 익명 내부 클래스를 명명된 로컬 내부 클래스로 변환합니다.
이 익명 내부 클래스에서 초기화 블록을 사용하십시오.
왜 내부 클래스가 필요한가요?
Java 내부 클래스의 이점은 무엇입니까? 내부 클래스가 필요한 이유는 무엇입니까?
먼저 간단한 예를 들어보겠습니다. 인터페이스를 구현하고 싶지만 이 인터페이스의 메소드가 구상한 클래스의 메소드와 동일한 이름과 매개변수를 갖고 있다면 어떻게 해야 할까요? 이때 이 인터페이스를 구현하기 위해 내부 클래스를 빌드할 수 있습니다. 내부 클래스는 외부 클래스의 모든 것에 액세스할 수 있으므로 그렇게 하면 인터페이스를 직접 구현했을 때 얻을 수 있는 모든 기능이 달성됩니다.
하지만 방법을 바꾸는 것만으로도 충분하지 않을까?
실제로 이것을 내부 클래스를 설계하는 이유로 사용하는 것은 실제로 설득력이 없습니다.
실제 이유는 Java의 내부 클래스와 인터페이스를 함께 사용하면 C++ 프로그래머가 다중 상속 없이 Java에서 자주 불평하는 문제를 해결할 수 있기 때문입니다. 실제로 C++의 다중 상속은 설계하기가 매우 복잡하며 Java는 내부 클래스와 인터페이스를 통해 다중 상속의 효과를 매우 잘 얻을 수 있습니다.