package nested.nested;
public class NestedOuter {
private static int outClassValue = 3;
private int outInstanceValue = 2;
static class Nested {
private int nestedInstanceValue = 1;
public void print() { // 자신의 멤버에 접근
System.out.println(nestedInstanceValue);
// 바깥 클래스의 인스턴스 멤버에는 접근할 수 없다.
// 바깥 클래스의 클래스 멤버에는 접근할 수 있다. private도 접근 가능
System.out.println(NestedOuter.outClassValue);
}
}
}
private
접근 제어자는 같은 클래스 안에 있을 때만 접근 할 수 있다.package nested.nested;
public class NestedOuterMain {
public static void main(String[] args) {
NestedOuter outer = new NestedOuter();
NestedOuter.Nested nested = new NestedOuter.Nested();
nested.print();
System.out.println("nestedClass = " + nested.getClass());
}
}
정적 중첩 클래스는 바깥 클래스와 서로 관계가 없다. 하지만 내부 클래스는 바깥 클래스의 인스턴스를 이루는 요소가 되어 내부 클래스는 바깥 클래스의 인스턴스에 소속된다.
package nested.inner;
public class InnerOuter {
private static int outClassValue = 3;
private int outInstanceValue = 2;
class Inner {
private int innerInstanceValue = 1;
public void print() {
// 자신의 멤버에 접근 System.out.println(innerInstanceValue);
// 외부 클래스의 인스턴스 멤버에 접근 가능, private도 접근 가능
System.out.println(outInstanceValue);
// 외부 클래스의 클래스 멤버에는 접근 가능. private도 접근 가능
System.out.println(InnerOuter.outClassValue);
}
}
}
private
접근제어자에도 접근 가능하다. package nested.inner;
public class InnerOuterMain {
public static void main(String[] args) {
InnerOuter outer = new InnerOuter();
InnerOuter.Inner inner = outer.new Inner();
inner.print();
System.out.println("innerClass = " + inner.getClass());
}
}
바깥 클래스의 인스턴스 참조.new 내부클래스()
로 생성 할 수 있다.내부클래스 활용 전
package nested.inner.ex1; //Car에서만 사용
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() + "의 엔진을 구동합니다.");
}
}
package nested.inner.ex1;
public class Car {
private String model;
private int chargeLevel;
private Engine engine;
public Car(String model, int chargeLevel) {
this.model = model;
this.chargeLevel = chargeLevel;
this.engine = new Engine(this);
}
//Engine에서만 사용하는 메서드
public String getModel() {
return model;
}
//Engine에서만 사용하는 메서드
public int getChargeLevel() {
return chargeLevel;
}
public void start() {
engine.start();
System.out.println(model + " 시작 완료"); }
}
}
내부 클래스 활용 후
package nested.inner.ex2;
public class Car {
private String model;
private int chargeLevel;
private Engine engine;
public Car(String model, int chargeLevel) {
this.model = model;
this.chargeLevel = chargeLevel;
this.engine = new Engine();
}
public void start() {
engine.start();
System.out.println(model + " 시작 완료");
}
private class Engine {
public void start() {
System.out.println("충전 레벨 확인: " + chargeLevel);
System.out.println(model + "의 엔진을 구동합니다."); }
}
}
}
Car
클래스의 정보들이 외부에 노출되는 것을 막아 캡슐화를 진행한다고 볼 수 있다.Engine.start()
Car
의 인스턴스 변수의 chargeLevel
에 직접 접근 할 수 있다.Car
의 인스턴스 변수인 model
에 직접 접근 할 수 있다.class Outer {
public void process() {
//지역 변수
int localVar = 0;
//지역 클래스
class Local {...}
Local local = new Local();
}
}
내부 클래스를 포함한 중첩 클래스들도 일반 클래스처럼 인터레이스를 구현하거나 부모 클래스를 상속 할 수 있다.
package nested.local;
public interface Printer {
void print();
}
package nested.local;
public class LocalOuterV2 {
private int outInstanceVar = 3;
public void process(int paramVar) {
int localVar = 1;
class LocalPrinter implements Printer {
int value = 0;
@Override
public void print() {
System.out.println("value=" + value);
System.out.println("localVar=" + localVar);
System.out.println("paramVar=" + paramVar);
System.out.println("outInstanceVar=" + outInstanceVar);
}
}
Printer printer = new LocalPrinter();
printer.print();
}
public static void main(String[] args) {
LocalOuterV2 localOuter = new LocalOuterV2();
localOuter.process(2);
}
}
package nested.local;
public class LocalOuterV3 {
private int outInstanceVar = 3;
public Printer process(int paramVar) {
int localVar = 1;
//지역 변수는 스택 프레임이 종료되는 순간 함께 제거된다.
class LocalPrinter implements Printer {
int value = 0;
@Override
public void print() {
System.out.println("value=" + value);
//인스턴스는 지역 변수보다 더 오래 살아남는다.
System.out.println("localVar=" + localVar);
System.out.println("paramVar=" + paramVar);
System.out.println("outInstanceVar=" + outInstanceVar);
}
}
Printer printer = new LocalPrinter();
//printer.print()를 여기서 실행하지 않고 Printer 인스턴스만 반환한다.
return printer;
}
public static void main(String[] args) {
LocalOuterV3 localOuter = new LocalOuterV3();
Printer printer = localOuter.process(2);
//printer.print()를 나중에 실행한다. process()의 스택 프레임이 사라진 이후에 실행
printer.print();
}
}
LocalPrinter
인스턴스는 process()
메서드 안에서 생성된다. 그리고 process()
에서 main()
으로 생성한 LocalPrinter
인스턴스를 반환하고 변수에 참조를 보관한다.LocalPrinter
인스턴스는 main()
이 종료될 때 까지 생존한다.process()
메서드가 종료되면 스택프레임이 제거되면서 사라진다.process()
메서드가 종료되어도 LocalPrinter
인스턴스는 계속 생존한다는 점이다.process()
가 종료된 후에 main()
메서드 안에서 LocalPrinter.print()
메서드를 호출한다.LocalPrinter
인스턴스에 있는 print()
메서드는 지역변수에 접근해야 하지만 메서드가 종료되어 해당 지역 변수들도 이미 제거된 상태이다.앞서 예제에서 본 인스턴스가 제거되는 시기와 지역변수가 제거되는 시기가 맞지않아 문제가 생길 수 있는데 이를 막기 위해 지역 변수 캡처가 실행된다.
LocalPrinter
인스턴스 생성 시도: 지역 클래스의 인스턴스를 생성 할 때 지역 클래스가 접근하는 지역 변수를 확인한다.캡처 변수의 값을 변경하면 안된다
package nested.local;
public class LocalOuterV2 {
private int outInstanceVar = 3;
public void process(int paramVar) {
int localVar = 1;
Printer printer = new Printer(){
int value = 0;
@Override
public void print() {
System.out.println("value=" + value);
System.out.println("localVar=" + localVar);
System.out.println("paramVar=" + paramVar);
System.out.println("outInstanceVar=" + outInstanceVar);
}
}
Printer printer = new LocalPrinter();
printer.print();
}
public static void main(String[] args) {
LocalOuterV2 localOuter = new LocalOuterV2();
localOuter.process(2);
}
}
new Printer(){body}
익명 클래스는 클래스의 본문을 정의하면서 동시에 생성한다. 이 코드는 마치 인터페이스 Printer
를 생성 한 것처럼 보인다. 하지만 자바에서는 인터페이스를 생성하는 것이 불가능 해, Printer
라는 이름의 인터페이스를 구현한 익명클래스를 생성하는 것이다. 쉡게 말해 Printer
인터페이스를 상속(구현)하면서 바로 생성하는 것이다.
익명 클래스의 특징
익명 클래스의 장점
익명 클래스를 사용하면 클래스를 별도로 정의하지 않고도 인터페이스나 추상 클래스를 즉석에서 구현할 수 있어 코드 가 더 간결해진다. 하지만, 복잡하거나 재사용이 필요한 경우에는 별도의 클래스를 정의하는 것이 좋다.
출처: https://www.inflearn.com/course/%EA%B9%80%EC%98%81%ED%95%9C%EC%9D%98-%EC%8B%A4%EC%A0%84-%EC%9E%90%EB%B0%94-%EC%A4%91%EA%B8%89-1/dashboard