for문을 중첩하는 것을 중첩 for문이라고 한다.
for (...) {
//중첩 for문
for (...) {
}
}
클래스 안에 클래스를 중첩해서 정의할 수 있는데 중첩 클래스라고 한다.
class Outer {
...
//중첩 클래스
class Nested {
...
}
}
중첩 클래스 종류

중첩 클래스는 -> 정적, 내부 (내부, 지역, 익명)
변수의 위치 -> 정적 변수, 인스턴스, 지역
중첩 클래스 위치 선언
-> 정적 중첩 클래스 -> 정적 변수와 위치 동일
-> 내부 클래스 -> 인스턴스 변수와 같은 위치
-> 지역 클래스 -> 지역 변수와 같은 위치
class Outer {
...
//정적 중첩 클래스
static class StaticNested {
...
}
//내부 클래스
class Inner {
...
}
}
정적 중첩 클래스는 정적 변수와 같이 앞에 static
내부 클래스는 인스턴스 변수와 같이 앞에 static
class Outer {
public void process() {
//지역 변수
int lcoalVar = 0;
//지역 클래스
class Local {...}
Local local = new Local();
}
}
지역 클래스는 코드 블럭 안에서 클래스 정의
익명 클래스는 지역 클래스의 특별한 버전
중첩 : 내부에 위치하거나 포함되는 구조적 관계
내부 : 내부에 있는 나를 구성하는 요소
중첩은 안에 있지만 내것이 아닌 것을 말한다.
단순히 위치만 안에 있는 것. 반면에 내부는 나의 내부에서 나를 구성하는 요소
중첩 클래스는 바깥 클래스의 안에 있지만 바깥 클래스와 관계 없는 전혀 다른 클래스
내부 클래스는 바깥 클래스의 내부에 있으면서 바깥 클래스를 구성하는 요소
중첩과 내부를 분류하는 핵심은 바깥 클래스 입장에서 볼때는 안에 있는 클래스가 나의 인스턴스 소속이 되는가 되지 않는가 차이
중첩과 내부를 분류하는 핵심은 바깥 클래스 입장에서 볼때 안에 있는 클래스가 나의 인스턴스 소속이 되는가 되지 않는가 차이
정적 중첩 클래스
static 이 붙는다.
바깥 클래스의 인스턴스에 소속되지 않는다.
내부 클래스
static 이 붙지 않는다.
바깥 클래스의 인스턴스에 소속된다
내부 클래스 종류
중첩 클래스: 정적 중첩 클래스 + 내부 클래스 종류 모두 포함
정적 중첩 클래스: 정적 중첩 클래스를 말함
내부 클래스: 내부 클래스, 지역 클래스, 익명 클래스를 포함해서 말함
내부 클래스를 포함된 모든 중첩 클래스는 특정 클래스가 다른 하나의 클래스 안에서만 사용, 둘이 아주 긴밀하게 연결 된 경우에만 사용
public class NestedOuter {
private static int outClassVal = 3;
private int outInstanceVal = 2;
static class Nested{
private int nestedInstanceVal = 1;
public void print(){
//자신의 멤버에 접근
System.out.println(nestedInstanceVal);
//바깥 클래스의 인스턴스 멤버에는 접근 할 수 X
//System.out.println(outInstanceVal);
//static이기 떄문에 접근 할 수 없음
//바깥 클래스의 클래스 멤버에는 접근할 수 있다.
//클래스 영역에 있다, private이어도 접근 가능
System.out.println(NestedOuter.outClassVal);
}
}
}
정적 중첩 클래스 앞에는 static
정적 중첩 클래스는
자신의 멤버에는 당연히 접근
바깥 클래스의 인스턴스 멤버에는 접근 X
바깥 클래스의 클래스 멤버에는 접근 O
private 접근 제어자
중첩 클래스도 바깥 클래스와 같은 클래스 안에 있다. 중첩 클래스는 바깥 클래스의 private 접근 제어자를 접근 할 수 있다.
public class NestedOuterMain {
public static void main(String[] args) {
NestedOuter outer = new NestedOuter();
NestedOuter.Nested nested = new NestedOuter.Nested();
nested.print();
System.out.println("nested.getClass() = " + nested.getClass());
}
}
정적 중첩 클래스는 new 바깥클래스.중첩클래스로 생성 가능
new NestedOuter() 로 만든 바깥 클래스의 인스턴스와 new NestedOuter.Nested() 로 만든
정적 중첩 클래스의 인스턴스는 서로 아무 관계가 없는 인스턴스이다. 단지 클래스 구조상 중첩해 두었을 뿐이다.

바깥 클래스의 멤버에 접근

public class Network {
public void sendMessage(String text){
NetworkMessage message = new NetworkMessage(text);
message.print();
}
}
public class NetworkMain {
public static void main(String[] args) {
Network network = new Network();
network.sendMessage("Hello");
}
}
public class NetworkMessage {
private String content;
public NetworkMessage(String content) {
this.content = content;
}
public void print(){
System.out.println(content);
}
}
public class Network {
public void sendMessage(String text){
NetworkMessage message = new NetworkMessage(text);
message.print();
}
private static class NetworkMessage {
private String content;
public NetworkMessage(String content) {
this.content = content;
}
public void print(){
System.out.println(content);
}
}
}
public class NetworkMain {
public static void main(String[] args) {
Network network = new Network();
network.sendMessage("Hello");
}
}
바깥클래스.중첩클래스 로 접근 해야 한다
NestedOuter.Nested nested = new NestedOuter.Nested();
나의 클래스에 포함된 중첩 클래스에 접근할 때는 바깥 클래스 이름을 적지 않아도 ok
public class Network {
public void sendMessage(String text) {
NetworkMessage networkMessage = new NetworkMessage(text);
}
private static class NetworkMessage {...}
}
정적 중첩 클래스는 바깥 클래스와 서로 관계 X. 내부 클래스는 바깥 클래스의 인스턴스를 이루는 요소가 된다.
public class InnerOuter {
private static int outClassVal = 3;
private int outInstanceVal = 2;
class Inner {
private int innerInstanceVal = 1;
public void print(){
//자기 자신 접근
System.out.println(innerInstanceVal);
//외부 클래스의 인스턴스 접근
System.out.println(outInstanceVal);
}
}
}
내부 클래스는 앞에 static 이 붙지 않는다. 쉽게 이야기해서 인스턴스 멤버가 된다.
내부 클래스는
private 접근 제어자
public class InnerOuterMain {
public static void main(String[] args) {
InnerOuter outer = new InnerOuter();
//인스턴스에 접근 할 수 있다 -> 인스턴스를 알아야 한다.
InnerOuter.Inner inner = outer.new Inner();
inner.print();
System.out.println("inner.getClass() = " + inner.getClass());
}
}
내부 클래스는 바깥 클래스의 인스턴스 소속, 바깥 클래스의 인스턴스 정보를 알아야 생성 가능
new 바깥클래스의 인스턴스 참조.내부클래스() 로 생성할 수 있다.
내부 클래스 생성

내부 인스턴스는 바깥 인스턴스를 알기 때문에 인스턴스의 멤버에 접근 가능

실제로 내부 인스턴스가 바깥 인스턴스 안에 생성 되는 것은 X
개념상 인스턴스 안에 생성 된다고 볼수 있다.
내부 인스턴스는 바깥 인스턴스의 참조를 보관.
public class Engine {
private Car car;
public Engine(Car car){
this.car = car;
}
public void start(){
System.out.println(car.getChargeLevel());
System.out.println(car.getModel());
}
}
public class Car {
private String model;
private int chargeLevel;
private Engine engine;
public Car(String model, int chargeLevel) {
this.model = model;
this.chargeLevel = chargeLevel;
//engine을 넘길때 car 넘긴다.
this.engine = new Engine(this);
}
public String getModel() {
return model;
}
public int getChargeLevel() {
return chargeLevel;
}
public void start(){
engine.start();
System.out.println(model);
}
}
public class CarMain {
public static void main(String[] args) {
Car car = new Car("X", 100);
car.start();
}
}
public class Car {
private String model;
private int chargeLevel;
private Engine engine;
public Car(String model, int chargeLevel) {
this.model = model;
this.chargeLevel = chargeLevel;
//engine을 넘길때 car 넘긴다.
this.engine = new Engine(this);
}
public String getModel() {
return model;
}
public int getChargeLevel() {
return chargeLevel;
}
public void start(){
engine.start();
System.out.println(model);
}
}
public class Engine {
private Car car;
public Engine(Car car){
this.car = car;
}
public void start(){
System.out.println(car.getChargeLevel());
System.out.println(car.getModel());
}
}
public class CarMain {
public static void main(String[] args) {
Car car = new Car("X", 100);
car.start();
}
}
리팩토링 전에는 결과적으로 모델 이름과 충전 레벨을 외부에 노출했다. 이것은 불필요한 Car 클래스의 정보들이 추가로 외부에 노출되는 것이기 때문에 캡슐화를 떨어뜨린다.
리팩토링 후에는 getModel() , getChargeLevel() 과 같은 메서드를 모두 제거했다. 결과적으로 꼭 필요한 메서드만 외부에 노출함으로써 Car 의 캡슐화를 더 높일 수 있었다
public class ShdowingMain {
public int val = 1;
class Inner{
public int val = 2;
void go(){
int val = 3;
System.out.println("val = " + val);
//나의 인스턴스 val
System.out.println("this.val = " + this.val);
//바깥 클래스 접근
System.out.println(ShdowingMain.this.val);
}
}
public static void main(String[] args) {
ShdowingMain main = new ShdowingMain();
Inner inner = main.new Inner();
inner.go();
}
}
변수 이름이 동일하기에 어떤 변수를 우선 사용할지 순위를 정해야 함.
프로그래밍에서 우선순위는 대부분 더 가깝거나 구체적인것 우선 순위.
다른 변수를 가리더라도 인스턴스의 참조를 사용하면 외부 변수에 접근 가능
this.value 는 내부 클래스의 인스턴스에 접근하고, 바깥클래스이름.this 는 바깥 클래스의 인스턴스에 접근할 수 있다