클로져
- 핵심은 자바스크립트나 자바에서 자신의 스코프의 범위 밖에 있는 변수에 접근하는 개념임.
- 자바같은경우 익명클래스, 람다가 있음. 자신을 선언하는 클래스의 멤버변수나 메쏘드 지역변수등 접근 가능함.
- 익명클래스와 람다의 스코프에는 조금 차이가 있는데, 핵심은 익명클래스는 말그대로 익명 "객체"인 것이고, 람다는 객체가 아니다! 즉, 익명클래스 내에서 this는 자기 자신을 가리키고, 람다에서 this는 자신을 선언한 클래스를 말함.
- Closure -> Close over한다고 해서 클로져임.
- int i = 100; 이고 callingMethod(x -> x * 2 + i) 이런식으로 메쏘드를 호출 할때, x는 람다표현식의 파라미터이고, i는 non-local variable(free variable) 이다. (참고로 x는 local variable, 또는 bound variable이라고함)
i 가 저 람다표현식의 close over한다고 한다. scope 자체를 외부로 확장한다. - 자바 8 이전에는 익명클래스의 함수에서 non-local variable에 접근하기 위해선, 이 non-local variable이 무조건 final이어야 했다.
- 자바8에서 final 키워드가 빠져도 되지만, 값 접근은 되지만 변경은 불가능. 이것을 effectively final 이라고 부른다. 결국은 final 이지만 안보일 뿐이다.
- 그리고 final이 안붙고, 값할당이 두번이상 된다면 이건 effectively final이 될 수 없으므로 사용 못함.
- 외부 쓰레드에서 값이 바뀔 염려도 있고 하여, 오라클 자바8 아키텍터가 final로 했다고 함.
- 예를들어 Object A; A변수가 있는데, 같은 클래스 내에서 A가 스코프 범위 밖의 변수(non-local variable, free variable)인데 익명클래스(클로저)나 람다식에서 A=B 이런식으로 할당이 가능해 버리면 A가 보고 있는 객체의 주소 자체가 바뀌게 되고, 이렇게 되면 사용자가 알지못하는 버그를 초래할 수 있으므로 사전에 방지하기 위함
- 람다식에서 non-local variable 사용시 값복사를 한다고 했는데, 아무튼 A주소에 접근하는게 아니라, A객체의 얕은복사 형태로 만들어서 사용하는 듯 함.
- 당연히 변수에서 접근하는 객체의 주소 자체를 바꿀순 없지만, 객체 내부의 값을 셋터를 통해 바꾸는것은 가능함.
쉐도잉
- 함수 밖의 함수가 가려지는것을 말함. (익명클래스의 클로져 문제)
- 예제코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
public class TestMain {
private int A = 180;
public static void main(String ...args) {
TestMain testMain = new TestMain();
testMain.run();
}
@Override
public String toString() {return "toString 오버라이드";}
public String toString(int i) {return "toString 오버로딩";}
void call1() {System.out.println("you called call1 method");}
public static void call2() {System.out.println("you called call2 method");}
public void noOverriding() {System.out.println("you called noOverriding method");}
public void noOverriding(int i) {System.out.println("you called noOverriding method, overriding");}
void run() {
int B = 39;
InterA interA = new InterA() {
int A = 130;
@Override
public String InterA() {
System.out.println(TestMain.this.A);
call2();
System.out.println(toString());
// toString은 쉐도잉이 일어남. (스코프 범위 밖에서 오버라이딩 메쏘드 구현되어있어도 찾지못함)
// System.out.println(toString(1));
//무조건 클래스이름.this.로 접근해야ㅐ함
System.out.println(TestMain.this.toString(1));
//noOverriding은 오버라이딩이 된 메쏘드가 아니므로 쉐도잉이 일어나지 않음.
noOverriding();
noOverriding(1);
return null;
}
};
interA.InterA();
}
}
interface InterA {
public abstract String InterA();
}
|
cs |
'IT > 자바8-람다' 카테고리의 다른 글
람다 내부 구현 설명 및 슈가, 디슈가링이란? (0) | 2020.05.07 |
---|---|
람다 원리 및 값 캡쳐란? (0) | 2020.05.07 |
Identity 함수? (0) | 2020.04.29 |
자주쓰는 람다 (0) | 2020.04.29 |
람다 사용법 및 규칙 (0) | 2020.04.23 |