class OuterClass {
...
static class StaticNestedClass {
...
}
class InnerClass {
...
}
}
이러한 내부 클래스를 중첩이라고 합니다. 두 가지 유형으로 나뉩니다.
- 비정적 중첩 클래스. 내부 클래스라고도 합니다.
- 정적 중첩 클래스.
- 지역 수업
- 익명 클래스
public class Bicycle {
private String model;
private int weight;
public Bicycle(String model, int weight) {
this.model = model;
this.weight = weight;
}
public void start() {
System.out.println("Let's go!");
}
public class Handlebar {
public void right() {
System.out.println("Steer right!");
}
public void left() {
System.out.println("Steer left!");
}
}
public class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
}
여기에 수업이 있습니다 Bicycle
. 2개의 필드와 1개의 메서드가 있습니다: start()
. 두 개의 클래스를 포함한다는 점에서 일반 클래스와 다릅니다. Handlebar
및 Seat
. 그들의 코드는 클래스 내부에 작성됩니다 Bicycle
. 이들은 본격적인 클래스입니다. 보시다시피 각각 고유 한 방법이 있습니다. 이 시점에서 질문이 생길 수 있습니다. 왜 우리는 한 클래스 안에 다른 클래스를 넣어야 할까요? 내부 클래스로 만드는 이유는 무엇입니까? 프로그램에서 핸들바와 좌석의 개념에 대해 별도의 클래스가 필요하다고 가정합니다. 물론 그것들을 중첩시킬 필요는 없습니다! 우리는 평범한 수업을 할 수 있습니다. 예를 들면 다음과 같습니다.
public class Handlebar {
public void right() {
System.out.println("Steer right!");
}
public void left() {
System.out.println("Steer left");
}
}
public class Seat {
public void up() {
System.out.println("Seat up!");
}
public void down() {
System.out.println("Seat down!");
}
}
아주 좋은 질문입니다! 물론 우리는 기술에 제한을 받지 않습니다. 그렇게 하는 것은 확실히 선택 사항입니다. 여기서 중요한 것은 특정 프로그램과 그 목적의 관점에서 클래스를 올바르게 설계하는 것입니다. 내부 클래스는 다른 엔터티와 불가분하게 연결된 엔터티를 분리하기 위한 것입니다. 핸들바, 좌석 및 페달은 자전거의 구성 요소입니다. 자전거와 분리하면 별 의미가 없습니다. 이러한 모든 개념을 별도의 공용 클래스로 만들면 프로그램에 다음과 같은 코드가 있을 것입니다.
public class Main {
public static void main(String[] args) {
Handlebar handlebar = new Handlebar();
handlebar.right();
}
}
흠... 이 코드의 의미는 설명하기조차 어렵습니다. 모호한 핸들 바가 있습니다 (왜 필요한가요? 솔직히 모르겠습니다). 그리고 이 핸들은 오른쪽으로... 자전거 없이... 저절로... 어떤 이유에서인지. 자전거의 개념에서 핸들바의 개념을 분리함으로써 우리는 프로그램에서 일부 논리를 잃었습니다. 내부 클래스를 사용하면 코드가 매우 다르게 보입니다.
public class Main {
public static void main(String[] args) {
Bicycle peugeot = new Bicycle("Peugeot", 120);
Bicycle.Handlebar handlebar = peugeot.new Handlebar();
Bicycle.Seat seat = peugeot.new Seat();
seat.up();
peugeot.start();
handlebar.left();
handlebar.right();
}
}
콘솔 출력:
Seat up!
Let's go!
Steer left!
Steer right!
이제 우리가 보는 것이 갑자기 이해가 됩니다! :) 자전거 오브젝트를 만들었습니다. 핸들바와 좌석이라는 두 개의 자전거 "하위 객체"를 만들었습니다. 우리는 편안함을 위해 좌석을 올리고 출발했습니다. 필요에 따라 페달링과 스티어링을 했습니다! :) 우리에게 필요한 메소드는 적절한 객체에서 호출됩니다. 모두 간단하고 편리합니다. 이 예에서는 핸들바와 좌석을 분리하여 캡슐화를 강화하고(관련 클래스 내부의 자전거 부품에 대한 데이터를 숨김) 더 자세한 추상화를 만들 수 있습니다. 이제 다른 상황을 살펴보겠습니다. 자전거 상점과 자전거 예비 부품을 시뮬레이트하는 프로그램을 만들고 싶다고 가정합니다. 이 상황에서는 이전 솔루션이 작동하지 않습니다. 자전거 매장에서는 자전거와 분리되어 있어도 각각의 개별 자전거 부품이 의미가 있습니다. 예를 들어 "고객에게 페달 판매", "새 좌석 구매" 등과 같은 메서드가 필요합니다. 여기서 내부 클래스를 사용하는 것은 실수입니다. 새 프로그램의 각 개별 자전거 부품에는 의미가 있습니다. 자체: 자전거의 개념과 분리할 수 있습니다. 내부 클래스를 사용해야 하는지 아니면 모든 엔터티를 별도의 클래스로 구성해야 하는지 궁금할 때 주의를 기울여야 할 사항입니다. 객체 지향 프로그래밍은 실제 엔터티를 쉽게 모델링할 수 있다는 점에서 좋습니다. 이는 내부 클래스를 사용할지 여부를 결정할 때 기본 원칙이 될 수 있습니다. 실제 매장에서는 예비 부품은 자전거와 분리되어 있습니다. 괜찮습니다. 이것은 프로그램을 설계할 때에도 괜찮다는 것을 의미합니다. 자, 우리는 "철학"을 알아냈습니다 :) 이제 내부 클래스의 중요한 "기술적" 기능에 대해 알아보겠습니다. 반드시 기억하고 이해해야 할 사항은 다음과 같습니다.
-
내부 클래스의 객체는 외부 클래스의 객체 없이 존재할 수 없습니다.
Seat
이것이 의미가 있습니다: 이것이 우리가 프로그램에서 및 내부 클래스를 만든 이유입니다Handlebar
. 그래서 우리는 고아 핸들바와 좌석으로 끝나지 않습니다.이 코드는 컴파일되지 않습니다.
public static void main(String[] args) { Handlebar handlebar = new Handlebar(); }
또 다른 중요한 기능은 다음과 같습니다.
-
내부 클래스의 개체는 외부 클래스의 변수에 액세스할 수 있습니다.
int seatPostDiameter
예를 들어 클래스에 변수(안장 기둥의 직경을 나타냄)를 추가해 보겠습니다Bicycle
.그런 다음 내부 클래스에서 좌석 속성을 표시하는 메서드를
Seat
만들 수 있습니다 .displaySeatProperties()
public class Bicycle { private String model; private int weight; private int seatPostDiameter; public Bicycle(String model, int weight, int seatPostDiameter) { this.model = model; this.weight = weight; this.seatPostDiameter = seatPostDiameter; } public void start() { System.out.println("Let's go!"); } public class Seat { public void up() { System.out.println("Seat up!"); } public void down() { System.out.println("Seat down!"); } public void displaySeatProperties() { System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
이제 프로그램에 이 정보를 표시할 수 있습니다.
public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); Bicycle.Seat seat = bicycle.new Seat(); seat.displaySeatProperties(); } }
콘솔 출력:
Seat properties: seatpost diameter = 40
메모:새 변수는 가장 엄격한 액세스 한정자(
private
)로 선언됩니다. 그리고 여전히 내부 클래스에 액세스할 수 있습니다! -
내부 클래스의 개체는 외부 클래스의 정적 메서드에서 만들 수 없습니다.
이는 내부 클래스가 구성되는 방식의 특정 기능으로 설명됩니다. 내부 클래스에는 매개변수가 있는 생성자가 있거나 기본 생성자만 있을 수 있습니다. 그러나 그럼에도 불구하고 내부 클래스의 객체를 생성할 때 외부 클래스의 객체에 대한 참조가 내부 클래스의 생성된 객체에 보이지 않게 전달됩니다. 결국 이러한 개체 참조의 존재는 절대적인 요구 사항입니다. 그렇지 않으면 내부 클래스의 개체를 만들 수 없습니다.
그러나 외부 클래스의 메서드가 정적이면 외부 클래스의 개체가 없을 수 있습니다! 그리고 이것은 내부 클래스가 작동하는 방식의 논리를 위반하는 것입니다. 이 상황에서 컴파일러는 오류를 생성합니다.
public static Seat createSeat() { // Bicycle.this cannot be referenced from a static context return new Seat(); }
-
내부 클래스는 정적 변수 및 메서드를 포함할 수 없습니다.
논리는 동일합니다. 정적 메서드와 변수가 존재할 수 있고 개체가 없는 경우에도 호출하거나 참조할 수 있습니다.
그러나 외부 클래스의 개체가 없으면 내부 클래스에 액세스할 수 없습니다.
명백한 모순! 이것이 정적 변수와 메서드가 내부 클래스에서 허용되지 않는 이유입니다.
생성하려고 하면 컴파일러에서 오류가 발생합니다.
public class Bicycle { private int weight; public class Seat { // An inner class cannot have static declarations public static void displaySeatProperties() { System.out.println("Seat properties: seatpost diameter = " + Bicycle.this.seatPostDiameter); } } }
-
내부 클래스의 개체를 만들 때 액세스 한정자가 중요합니다.
내부 클래스는 표준 액세스 수정자(
public
,private
,protected
및 ) 로 표시할 수 있습니다package private
.이것이 왜 중요한가요?
이는 프로그램에서 내부 클래스의 인스턴스를 생성할 수 있는 위치에 영향을 미칩니다.
Seat
클래스가 로 선언 되면 다른 클래스에서 객체를public
만들 수 있습니다 .Seat
유일한 요구 사항은 외부 클래스의 개체도 존재해야 한다는 것입니다.그건 그렇고, 우리는 이미 여기에서 이것을 했습니다:
public class Main { public static void main(String[] args) { Bicycle peugeot = new Bicycle("Peugeot", 120); Bicycle.Handlebar handlebar = peugeot.new Handlebar(); Bicycle.Seat seat = peugeot.new Seat(); seat.up(); peugeot.start(); handlebar.left(); handlebar.right(); } }
Handlebar
클래스 에서 내부 클래스 에 쉽게 액세스할 수 있었습니다Main
.내부 클래스를 로 선언하면
private
외부 클래스 내부에서만 객체를 생성할 수 있습니다.Seat
더 이상 "외부에" 개체를 만들 수 없습니다 .private class Seat { // Methods } public class Main { public static void main(String[] args) { Bicycle bicycle = new Bicycle("Peugeot", 120, 40); // Bicycle.Seat has private access in Bicycle Bicycle.Seat seat = bicycle.new Seat(); } }
당신은 이미 논리를 이해했을 것입니다 :)
-
내부 클래스에 대한 액세스 한정자는 일반 변수와 동일하게 작동합니다.
수정
protected
자는 동일한 패키지에 있는 하위 클래스 및 클래스의 인스턴스 변수에 대한 액세스를 제공합니다.protected
내부 클래스에서도 작동합니다.protected
내부 클래스의 객체를 만들 수 있습니다 .- 외부 클래스에서;
- 하위 클래스에서;
- 동일한 패키지에 있는 클래스에서.
내부 클래스에 액세스 한정자( )가 없으면
package private
내부 클래스의 개체를 만들 수 있습니다.- 외부 클래스에서;
- 동일한 패키지에 있는 클래스에서.
오랫동안 수식어에 익숙했으므로 여기서는 문제가 없습니다.