final์ ์๋์ง๋ง final์ฒ๋ผ
์๋ฐ์์ final ํค์๋๊ฐ ์ ์ธ๋์ง ์์ ๋ณ์์ง๋ง, ๊ฐ์ด ์ฌํ ๋น๋์ง ์์ final ๊ณผ ์ ์ฌํ๊ฒ ๋์ํ๋ ๊ฒ์ effectively final์ด๋ผ๊ณ ํ๋ค.
์ด ๊ฐ๋
์ ์๋ฐ 8์์ ๋์
๋์๋๋ฐ, ์ต๋ช
ํด๋์ค(Anonymous Classes) ๋๋ ๋๋ค์(Lambda Expressions)์ด ์ฌ์ฉ๋ ์ฝ๋์์ ์ฝ๊ฒ ์ฐพ์๋ณผ ์ ์๋ค.
์ต๋ช
ํด๋์ค ๋๋ ๋๋ค์์์๋ ์ฐธ์กฐํ๋ ์ธ๋ถ ์ง์ญ ๋ณ์๊ฐ final๋ก ์ ์ธ๋๊ฑฐ๋ ์ ์ธ๋ ํ ์ฐธ์กฐ๊ฐ ๋ณ๊ฒฝ๋์ง ์๋ effectively final์ธ ๊ฒฝ์ฐ์๋ง ์ ๊ทผ ๊ฐ๋ฅํ๋ค.
์๋ฅผ ๋ค์ด ์๋ ์์ ์ ๊ฐ์ด ์ฐธ์กฐํ๋ ์ง์ญ ๋ณ์๊ฐ ๋ด๋ถ์์ ๋ณ๊ฒฝ๋๋ค๋ฉด โlocal variables referenced from a lambda expression must be final or effectively finalโ ์ค๋ฅ ๋ฉ์์ง์ ํจ๊ป ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
// Anonymous Classes
public void someMethod() {
int count = 0;
Runnable runnable = new Runnable() {
@Override
public void run() {
// "local variables referenced from an inner class
// must be final or effectively final"
count++;
}
};
}
// Lambda Expressions
public void someMethod() {
List<Integer> list = Arrays.asList(1, 2, 3, 4);
Integer criteria;
for (Integer integer : list) {
if (integer > 2) {
criteria = 3;
// "local variables referenced from a lambda expression
// must be final or effectively final"
list.removeIf(o -> o.equals(criteria));
}
}
}
effectively final
๊ทธ๋ ๋ค๋ฉด ์ ํํ ์ด๋ค ๊ฒฝ์ฐ๋ฅผ effectively final์ด๋ผ๊ณ ๋งํ๋ ๊ฒ์ผ๊น? ์๋ฐ ์ธ์ด ์คํ์ ์ดํด๋ณด๋ฉด ๋ค์๊ณผ ๊ฐ์ ์กฐ๊ฑด์ ๋ง์กฑํ๋ ์ง์ญ ๋ณ์(local variables)๋ effectively final๋ก ๊ฐ์ฃผํ๋ค.
final๋ก ์ ์ธ๋์ง ์์๋ค.- ์ด๊ธฐํ๋ฅผ ์งํํ ํ์ ๋ค์ ํ ๋นํ์ง ์์๋ค.
- ์ ์(prefix) ๋๋ ํ์(postfix)์ ์ฆ๊ฐ ๋๋ ๊ฐ์ ์ฐ์ฐ์๊ฐ ์ฌ์ฉ๋์ง ์์๋ค.
์ฐธ๊ณ : โJava Docs: 4.12.4. final Variablesโ
๊ฐ์ฒด์ ๊ฒฝ์ฐ์๋ ๊ฐ์ฒด๊ฐ ๊ฐ๋ฆฌํค๋ ์ฐธ์กฐ๋ฅผ ๋ณ๊ฒฝํ์ง ์์ผ๋ฉด ๋๋ค. ๋ฐ๋ผ์ ์๋์ ๊ฐ์ด ๊ฐ์ฒด์ ์ํ๋ฅผ ๋ณ๊ฒฝํ๋๋ผ๋ effectively final์ด๋ค.
List<Person> personList = List.of(new Person(2), new Person(3));
for (Person p : personList) {
p.setId(2);
personList.removeIf(o -> o.getId() == p.getId());
}
Lambda Capturing
๋๋ค์์๋ ์ธ๋ถ์ ์ ์๋ ๋ณ์๋ฅผ ์ฌ์ฉํ ๋ ๋ด๋ถ์์ ์ฌ์ฉํ ์ ์๋๋ก ๋ณต์ฌ๋ณธ์ ์์ฑํ๋ค. ์ด๋ฅผ ๋๋ค ์บก์ฒ๋ง(Lambda Capturing)์ด๋ผ๊ณ ํ๋๋ฐ ์ฌ๊ธฐ์ ์ธ๋ถ ๋ณ์๋ ์ง์ญ ๋ณ์๋ฅผ ๋น๋กฏํ์ฌ ์ธ์คํด์ค ๋ณ์์ ํด๋์ค ๋ณ์๋ฅผ ํฌํจํ๋ค.
์ธ๋ถ ๋ณ์๋ฅผ ์ฌ์ฉํ๋ ๋๋ค์(Capturing Lambda) ์์ ๋ฅผ ์ดํด๋ณด์. ์ฒซ ๋ฒ์งธ ์์ ๋ ์ธ๋ถ์ ์ ์ธ๋ ์ธ์คํด์ค ๋ณ์๋ฅผ ์ฐธ์กฐํ๊ณ ๋ ๋ฒ์งธ ์์ ๋ ๋๋ค ์ธ๋ถ์ ์ ์ธ๋ ์ง์ญ ๋ณ์๋ฅผ ์ฐธ์กฐํ๋ค.
// Capturing Lambda ์์ 1: ์ธ๋ถ ์ธ์คํด์ค ๋ณ์ ์ฐธ์กฐ
public class Tester {
private int count = 0;
public void someMethod() {
Runnable runnable = () -> System.out.println("count: " + count);
}
}
// Capturing Lambda ์์ 2: ์ธ๋ถ ์ง์ญ ๋ณ์ ์ฐธ์กฐ
public void someMethod() {
int count = 0;
Runnable runnable = () -> System.out.println(count);
}
์ด์ ๋ฐ๋๋ก ๋๋ค ๋ด๋ถ์์ ์ ๊ทผํ๋ ์ธ๋ถ ๋ณ์๊ฐ ์๋(Non-Capturing Lambda) ์์ ๋ ์๋์ ๊ฐ๋ค.
// Non-Capturing Lambda
Runnable runnable = () -> {
String msg = "Taengtest";
System.out.println(msg)
};
// Non-Capturing Lambda
Function<Integer, Integer> func = (param) -> 5 * param;
func.apply(5);
์ ๋ณต์ฌ๋ณธ์ ๋ง๋ค๊น?
๊ทธ๋ ๋ค๋ฉด ๋๋ค์ ๋ด๋ถ์์ ์ฐธ์กฐํ๋ ์ธ๋ถ ๋ณ์๋ฅผ ์บก์ฒ๋งํ๋ ์ด์ ๋ ๋ฌด์์ผ๊น? ์ด๋ ์ฐธ์กฐํ๋ ์ธ๋ถ ๋ณ์๊ฐ ์ง์ญ ๋ณ์์ผ ๋ ์กฐ๊ธ ๋ ๋ช ํํ๊ฒ ์ดํดํ ์ ์๋ค.
์ง์ญ ๋ณ์๋ ๋ฉ๋ชจ๋ฆฌ ์์ญ ์ค ์คํ(Stack)์ ํ ๋น๋๋ค. ์คํ ์์ญ์ ์ค๋ ๋๋ง๋ค ์์ ๋ง์ ๊ณ ์ ํ ์์ญ์ ๊ฐ๋ ํน์ฑ์ ๊ฐ๋๋ค. ๋ฐ๋ผ์ ์ค๋ ๋๋ผ๋ฆฌ ๊ณต์ ํ ์ ์์ผ๋ฉฐ ์ค๋ ๋๊ฐ ์ข ๋ฃ๋๋ ๊ฒฝ์ฐ ์์ฑ๋ ์คํ ์์ญ๋ ์ฌ๋ผ์ง๊ฒ ๋๋ค. ๋ฐ๋ผ์ ์ธ๋ถ ์ง์ญ ๋ณ์๋ฅผ ๊ทธ๋๋ก ์ฐธ์กฐํ์ง ๋ชปํ๊ธฐ ๋๋ฌธ์ ๋ณต์ฌ๋ณธ์ ์์ฑํ๋ ๊ฒ์ด๋ค.
๋ง์ฝ์ ๋ณต์ฌ๋ณธ์ ๋ง๋ค์ง ์๋ ๊ฒฝ์ฐ๋ ์ด๋ป๊ฒ ๋ ๊น? ์๋ ์ฝ๋๋ ์ ์์ ์ผ๋ก ์ปดํ์ผ ๋๋ ์ฝ๋์ง๋ง ๊ธฐ์กด์ ์๋ฐ ์คํ๊ณผ๋ ๋ค๋ฅด๊ฒ ์ธ๋ถ ์ง์ญ ๋ณ์๋ฅผ ์บก์ฒ๋งํ์ง ์๋๋ค๊ณ ๊ฐ์ ํด๋ณด์.
public void test() {
// local variable
int count = 0;
new Thread(() -> {
try {
// `count` ๋ฅผ ๋ณต์ฌํ์ง ์๋๋ค๊ณ ๊ฐ์
Thread.sleep(1000);
System.out.println("count :" + count);
} catch (InterruptedException e) {
// Exception Handling
}
}).start();
System.out.println("count :" + count);
}
์ ์ฝ๋๊ฐ ์ ์ ๋์ํ๋ค๋ฉด ์ด๋ป๊ฒ ๋ ๊น? ์์ ์ฒ๋ผ ๋๋ค๋ ๋ณ๋์ ์ค๋ ๋์์ ์ํ๋ ์ ์๋ค. ๋ํ ์์ ์ค๋ช ํ ๊ฒ์ฒ๋ผ ๊ฐ ์ค๋ ๋๋ง๋ค ๊ณ ์ ํ ์คํ ์์ญ์ ๊ฐ์ง๋ฉฐ, ์ง์ญ ๋ณ์๋ ์คํ ์์ญ์ ํ ๋น๋๋ค.
๋ฐ๋ผ์ test ๋ฉ์๋๋ฅผ ์คํํ๋ ์ค๋ ๋๋ ๋๋ค์์ ์คํํ๋ ์ค๋ ๋๊ฐ ๋๋๊ธฐ๋ ์ ์ ์คํ ์์ญ์์ ์ฌ๋ผ์ง ์ ์๋ค.
์ฆ, ๋๋ค ๋ด๋ถ์์ ๋ฉ์๋์ ์ ์ธ๋ ์ง์ญ ๋ณ์์ธ count๋ฅผ ์ฐธ์กฐํ์ง ๋ชปํ๋ ๊ฒฝ์ฐ๊ฐ ๋ฐ์ํ ์ ์๋ค.
์ ๋๋ค์์ ์ธ๋ถ ์ง์ญ ๋ณ์์ ๊ฐ์ ๋ณ๊ฒฝํ ์ ์์๊น?
๊ทธ๋ ๋ค๋ฉด ๋๋ค ๋ด๋ถ์์ ์ธ๋ถ ์ง์ญ ๋ณ์์ ๊ฐ์ ๋ณ๊ฒฝํ๋ ค๊ณ ํ๋ฉด ์ปดํ์ผ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ ์ด์ ๋ ๋ฌด์์ผ๊น? โ๋ณต์ฌ๋ณธ์ ์์ฑํ๋ค๋ฉด, ๊ฐ์ ๋ณ๊ฒฝํด๋ ๊ด์ฐฎ์ง ์์๊น?โ๋ผ๊ณ ์๊ฐํ ์๋ ์๋ค.
๋๋ค์์ ์์ ์ดํด๋ณธ ๊ฒ์ฒ๋ผ ๋ณ๋ ์ค๋ ๋์์ ์ํ์ด ๊ฐ๋ฅํ๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ์ธ๋ถ ์ง์ญ ๋ณ์๋ฅผ ์ ์ดํ๋ ์ค๋ ๋์ ๋๋ค์์ ์ํํ๋ ์ค๋ ๋๊ฐ ์๋ก ๋ค๋ฅผ ์ ์๋ค.
๋ ๋ค๋ฅธ ์์ ๋ฅผ ์ดํด๋ณด์. ๋ค๋ง ์ด๋ฒ ์์ ์ฝ๋๋ ์ปดํ์ผ ์ค๋ฅ๊ฐ ๋ฐ์ํ๋ค. ํ์ง๋ง ์ธ๋ถ ์ง์ญ ๋ณ์์ ๊ฐ์ ๋ณ๊ฒฝํ๋ฉด ์ ๋๋ ์ด์ ๋ฅผ ์ดํดํ๊ธฐ ์ํด ์ ์์ ์ผ๋ก ์คํ๋๋ค๊ณ ๊ฐ์ ํด๋ณด์. ์๋ ์ฝ๋๊ฐ ์ ์์ ์ผ๋ก ์ปดํ์ผ ๋๊ณ ์คํ๋ ๋์ ๋ฌธ์ ๋ ๋ฌด์์ผ๊น?
public class Tester {
ExecutorService executor = Executors.newFixedThreadPool(1);
public void testMultiThreading() {
// ์ค๋ ๋ A
boolean doLoop = true;
executor.execute(() -> {
// ์ค๋ ๋ B
while (doLoop) {
// something to do
}
});
doLoop = false;
}
}
์ ์์ ์์๋ ๋ ๊ฐ์ ์ค๋ ๋๊ฐ ์กด์ฌํ๋๋ฐ, ํ๋๋ ์ง์ญ ๋ณ์๋ฅผ ์ ์ดํ๋ ์ค๋ ๋์ด๊ณ ๋ค๋ฅธ ํ๋๋ ๋๋ค์์ ์คํํ๋ ์ค๋ ๋๋ค.
๊ทธ๋ฆฌ๊ณ ์์ ์ค๋ช
ํ ๊ฒ์ฒ๋ผ ๋๋ค์์๋ ์ธ๋ถ ์ง์ญ ๋ณ์๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ ์บก์ฒ๋ง์ ํ๊ฒ ๋๊ธฐ ๋๋ฌธ์ ๋๋ค์์ ์คํํ๋ ์ค๋ ๋์์๋
์ง์ญ ๋ณ์ doLoop์ ๊ฐ์ ์ฐธ์กฐํ๊ธฐ ์ํด ์ด๋ฅผ ๋ณต์ฌํ๋ค.
๋ฌธ์ ๋ ์ด ๋ถ๋ถ์์ ๋ฐ์ํ๋ค. ๋ณต์ฌ๋๋ ๊ฐ์ธ ์ธ๋ถ ์ง์ญ ๋ณ์๊ฐ ๋ณ๊ฒฝ ๊ฐ๋ฅํ๊ฒ ๋๋ ๊ฒฝ์ฐ ๋ณต์ฌ๋ ๊ฐ์ด ์ต์ ๊ฐ์์ ๋ณด์ฅํ ์ ์๋ค. ์ด๋ ๋ณ์์ ๊ฐ์์ฑ(visibility)๊ณผ๋ ์ฐ๊ด์ด ์๋๋ฐ, ์คํ ์์ญ์ ์ค๋ ๋๋ง๋ค ์์ฑ๋๊ธฐ ๋๋ฌธ์ ์ค๋ ๋ A, B๊ฐ ๊ฐ๋ ์คํ ์์ญ์ ๊ฐ์ ๊ณ ์ ํ๋ค. ๋ฐ๋ผ์ ํ ์ค๋ ๋์์ ๋ค๋ฅธ ์ค๋ ๋์ ์คํ์ ์๋ ๊ฐ์ ๋ณ๊ฒฝ์ฌํญ์ ํ์ธํ ์๊ฐ ์๋ค.
๋ฐ๋ผ์ ์์ ๊ฐ์ ์์ ๊ฐ ์ ์์ ์ผ๋ก ์ปดํ์ผ๋๊ณ ์คํ๋๋ค๋ฉด ๋ณต์ฌ๋ ๊ฐ์ ๋ณด์ฅํ ์ ์์ผ๋ฏ๋ก ๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ฌ ๊ฒฐ๊ณผ๋ฅผ ์์ธกํ ์ ์๋ ์ํฉ์ด ๋ฐ์ํ๋ค. ์ด๊ฒ์ด ๋๋ค์์์ ์ฐธ์กฐํ๋ ์ธ๋ถ ์ง์ญ ๋ณ์๊ฐ ๊ฐ์ด ๋ณ๊ฒฝ๋์ง ์์์ผ ํ๋ ์ด์ ๋ค.
๊ทธ๋ ๋ค๋ฉด ์ธ์คํด์ค ๋ณ์์ ํด๋์ค ๋ณ์๋?
๋จผ์ ์ธ์คํด์ค ๋ณ์์ ํด๋์ค ๋ณ์๊ฐ ๋ฌด์์ธ์ง ์ด๋ค์ ์ ์์ ๋ํด์ ์์๋ณด์.
์ธ์คํด์ค ๋ณ์๋ ํด๋์ค์ ์ ์ธ๋ ๋ณ์๋ฅผ ๋งํ๋ฉฐ ์ธ์คํด์ค ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ ํ(heap) ์์ญ์ ํ ๋น๋๋ค. ๊ทธ๋ฆฌ๊ณ ํด๋์ค ๋ณ์๋ ํด๋์ค์ ์ ์ธ๋ static ๋ณ์๋ฅผ ๋งํ๋ฉฐ ์ธ์คํด์ค ์์ฑ ์์ด ๋ฐ๋ก ์์ฑ๋๋ฉฐ ๋ฉ์๋(method) ์์ญ์ ์ ์ธ๋๋ค.
๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ํ ๋น๋ ๋ฉ๋ชจ๋ฆฌ ์์ญ์ด ์ง์ญ ๋ณ์๊ฐ ํ ๋น๋๋ ์คํ ์์ญ๊ณผ ๋ค๋ฅด๊ฒ ๋ฐ๋ก ํ์๋์ง ์์ ๋ณต์ฌํ๋ ๊ณผ์ ์ด ๋ถํ์ํ๋ค. ๋ฐ๋ผ์ ์๋์ ๊ฐ์ ์ฝ๋๋ ์ ์์ ์ผ๋ก ์ปดํ์ผ ๋๋ค.
public class Tester {
private int instanceVariable = 0;
private static int staticVariable = 0;
public void someMethodWithStaticVariable() {
instanceVariable = 1;
Runnable runnable = () -> {
instanceVariable++;
};
}
public void someMethodWithInstanceVariable() {
staticVariable = 1;
Runnable runnable = () -> {
staticVariable++;
};
}
}
์ ๋ฆฌํ๋ฉด
๋๋ค์ ๋ด๋ถ์์ ์ธ๋ถ ์ง์ญ ๋ณ์๋ฅผ ์ฐธ์กฐํ๋ ๊ฒฝ์ฐ final ๋๋ effectively final์ด์ด์ผ ํ๋ค.
์ด๋ฌํ ์ด์ ๋ ์ง์ญ ๋ณ์๊ฐ ๋ฉ๋ชจ๋ฆฌ ์์ญ์ค ์คํ(Stack) ์์ญ์ ํ ๋น๋๋ ๊ฒ๊ณผ ๊ด๋ จ์ด ์๋ค.
์คํ ์์ญ์ ์ค๋ ๋ ๋ณ๋ก ๊ณ ์ ํ๊ธฐ ๋๋ฌธ์ ์ง์ญ ๋ณ์๊ฐ ํ ๋น๋ ์ค๋ ๋๊ฐ ์ข ๋ฃ๋๋ฉด ์ง์ญ ๋ณ์๋ฅผ ๋์ด์ ์ฐธ์กฐํ์ง ๋ชปํ๊ฒ ๋๋ค. ๋ฐ๋ผ์ ๋ณ๋ ์ค๋ ๋์์ ์คํ ๊ฐ๋ฅํ ๋๋ค์์๋ ์ธ๋ถ ์ง์ญ ๋ณ์๋ฅผ ๋ณต์ฌํ๋ ๊ณผ์ ์ ๊ฑฐ์น๋๋ฐ, ๋ณต์ฌ๋๋ ๊ฐ์ด ๋ณ๊ฒฝ ๊ฐ๋ฅํ๋ค๋ฉด ์ฐธ์กฐํ๋ ๋ณ์์ ์ต์ ๊ฐ์ ๋ณด์ฅํ ์ ์์ด ๋ฉํฐ ์ค๋ ๋ ํ๊ฒฝ์์ ๋์์ฑ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
๋ฐ๋ผ์, ๋๋ค ๋ด๋ถ์์ ์ธ๋ถ ์ง์ญ ๋ณ์๋ฅผ ์ฐธ์กฐํ ๋๋ ๋ฐ๋์ fianl ๋๋ effectively final์ด์ด์ผ ํ๋ค.