지역 클래스
- 지역 클래스 : 내부 클래스 중 한 종류로 지역 변수와 같이 코드 블럭 안에 정의된다.
- 지역 변수에 접근할 수 있다.
- 자신이 속한 코드 블록의 매개 변수에도 접근할 수 있다. (매개 변수도 지역 변수)
- 지역 클래스는 지역 변수와 같이 접근 제어자를 사용할 수 없다.
- 지역 클래스도 클래스의 일종이기 때문에 인터페이스를 구현하거나 부모 클래스를 상속받을 수 있다.
지역 클래스 - 지역 변수 캡처
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);
}
}
LocalPrinter localPrinter = new LocalPrinter();
return localPrinter;
}
public static void main(String[] args) {
LocalOuterV3 localOuterV1 = new LocalOuterV3();
Printer printer = localOuterV1.process(2);
printer.print();
}
}
- 지역 클래스는 지역 변수에 접근할 수 있다.
print()에서 지역 변수 localVar와 매개 변수 paramVar에 접근해 사용했다.- 지역 변수는 스택 영역에 존재한다.
지역 변수의 생명 주기는 해당 코드 블록(스택 프레임)이 종료 시 함께 제거된다. - 인스턴스의 생명 주기는 해당 인스턴스를 아무도 참조하지 않을 때(GC 발생 전)까지 존재한다.
- 지역 변수는 스택 영역에 존재한다.
- 예제의 경우 main()에서 process()를 호출한다.
process()가 종료될 때 process() 내부의 지역 변수들도 함께 제거된다.
그 이후 process() 내부에 존재하는 지역 클래스의 print()를 통해 process() 내부의 지역 변수를 참조한다.
스택 프레임이 종료되어 지역 변수도 제거됐지만 정상적인 참조가 가능하다.
- 지역 변수가 제거된 시점에 지역 클래스에서 참조할 수 있는 이유는?
- 지역 변수 캡쳐로 인해서 참조가 가능해졌기 때문이다.
- 지역 변수 캡처 : 지역 클래스의 인스턴스를 생성하는 시점에 필요한 지역 변수를 복사해서 인스턴스에 저장한다.
- 모든 지역 변수가 아닌 접근이 필요한 지역 변수만 캡처한다.
- 위 예제에서는 필요한 지역 변수인 localVar와 매개 변수 paramVar를 인스턴스에 캡처해둔다.
지역 클래스에서 지역 변수를 참조할 때 스택 영역이 아닌 캡처한 변수에 접근한다. - 지역 변수 캡처를 통해 지역 변수와 지역 클래스의 인스턴스 생명 주기가 서로 다른 문제를 해결한다.
- ★지역 클래스가 접근하는 지역 변수는 절대로 중간에 값이 변하면 안된다★
- final로 선언해야 하거나, 사실상 final이어야 한다.
- 지역 변수의 값이 변경되게 되면 스택 영역의 지역 변수의 값과 인스턴스에 캡처한 변수의 값이 서로 달라지는 동기화 문제가 발생할 수 있다.
- 예상치 못한 곳에서 값이 변경되어 디버깅을 어렵게 할 수 있고 멀티 쓰레드 상황에서 이런 동기화는 더욱이 어렵다.
사실상 final (effectively final) : 사실상 final 지역 변수는 지역 변수에 final 키워드를 사용하지는 않았지만 값을 변경하지 않는 지역 변수를 의미한다.
익명 클래스 (Anonymous)
- 익명 클래스 : 지역 클래스의 종류 중 하나로 이름이 없는 클래스를 의미한다.
- 클래스의 이름을 생략하고 클래스의 선언과 생성을 한 번에 처리할 수 있다.
public interface Printer {
void print();
}
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);
}
};
- 익명 클래스는 클래스의 본문을 정의하면서 동시에 생성한다.
- new 다음에 바로 상속 받으면서 구현 할 부모 타입을 입력한다.
- new 인터페이스명() {구현 body};
- 익명 클래스는 변수에 참조 값을 담아서 인스턴스를 생성할 수도 있지만 인수로 바로 넘겨줄 수도 있다.
ex) run(new Car{구현});
- 익명 클래스의 특징
- 이름 없는 지역 클래스를 선언하면서 동시에 생성한다.
- 부모 클래스를 상속 받거나, 인터페이스를 구현해야 한다. (상위 클래스나 인터페이스가 필요하다.)
- 이름이 없기 때문에 생성자를 가질 수 없다. (기본 생성자만 사용된다.)
- 익명 클래스는 자바 내부에서 외부 클래스 이름 + $ + 숫자로 표현된다.
ex) 외부 클래스 = Outer 일 때, Outer$1, Outer$2 이런 식으로 숫자가 증가하면서 구분된다. - 클래스를 별도로 구현하지 않고 즉석에서 구현해서 사용할 수 있어서 코드가 간결해진다.
- 복잡하거나 재사용이 필요한 경우에 익명 클래스를 사용하는 것보다 지역 클래스를 사용하는 것이 더 좋다.
- 익명 클래스의 인스턴스는 단 한 번만 생성할 수 있기 때문에
- 프로그래밍에서 중복 제거, 좋은 코드를 유지하는 핵심
- 변하는 부분과 변하지 않는 부분을 분리하고 변하는 부분을 외부에서 전달 받음으로 메소드의 재사용을 높일 수 있다.
- 문자열과 같은 데이터를 메소드에 전달할 때는 각 데이터에 맞는 타입을 전달하면된다.
- 코드 조각을 메소드에 전달할 때는 인스턴스를 전달하고 해당 인스턴스에 있는 메소드를 호출한다.
- 변하는 부분을 같은 타입의 인터페이스로부터 메소드를 구현해놓고 다형성을 활용해 외부에서 전달되는 인스턴스에 따라서 다른 코드 조각이 실행될 수 있게 한다.
public class Ex1RefMainV1 {
static class Dice implements Process {
@Override
public void run() {
int randomValue = new Random().nextInt(6) + 1;
System.out.println("주사위 = " + randomValue);
}
}
static class Sum implements Process {
@Override
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("i = " + i);
}
}
}
public static void hello(Process process) {
System.out.println("프로그램 시작");
//코드 조각 시작
process.run();
//코드 조각 종료
System.out.println("프로그램 종료");
}
public static void main(String[] args) {
hello(new Dice());
hello(new Sum());
}
}
- 중복을 처리하기 위해서 단순 데이터 메소드에 전달하는 것이 아닌 코드 조각을 메소드에 전달하기 위한 방법.
- 람다 (Lambda)
- Java 8 이전까지는 기본형과 참조형만 메소드 인수로 전달할 수 있었다.
- 람다 : 메소드를 인수로 전달하는 것.
hello(() -> {
int randomValue = new Random().nextInt(6) + 1;
System.out.println("주사위 = " + randomValue);
});
hello(() -> {
for (int i = 0; i < 3; i++) {
System.out.println("i = " + i);
}
});
- 클래스나 인스턴스를 정의하지 않고 메소드의 코드 블록을 직접 인수로 전달한다.
- 람다와 익명 클래스의 차이는?
- 익명 클래스: 내부에서 멤버 변수를 선언해서 사용할 수 있다, 인스턴스를 통해서 인스턴스 내부의 메소드를 수행하는 방식
- 람다: 내부에서 멤버 변수를 선언해서 사용할 수 없다, 인스턴스를 따로 정의하지 않고 코드 블록 자체를 인수로 전달해서 수행하는 방식
출처: [인프런 김영한 실전 자바 - 중급편]
김영한의 실전 자바 - 중급 1편 강의 | 김영한 - 인프런
김영한 | 실무에 필요한 자바의 다양한 중급 기능을 예제 코드로 깊이있게 학습합니다., 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문법을
www.inflearn.com
'Java > [인프런 김영한 실전 자바 - 중급편]' 카테고리의 다른 글
[인프런 김영한 실전 자바 - 중급편] Generic 1, 2 (2) | 2024.07.25 |
---|---|
[인프런 김영한 실전 자바 - 중급편] 예외 처리1, 2 (0) | 2024.07.23 |
[인프런 김영한 실전 자바 - 중급편] 중첩 클래스, 내부 클래스1 (2) | 2024.07.22 |
[인프런 김영한 실전 자바 - 중급편] 날짜와 시간 (1) | 2024.07.21 |
[인프런 김영한 실전 자바 - 중급편] 열거형 - ENUM (0) | 2024.07.19 |