본문 바로가기

Dev.BackEnd/JAVA

[Java Adv] 16. Lambda Expression + Inner class, anonymous class

내부클래스란(inner class)?
말 그대로 클래스 내부에 선언된 클래스를 말한다.
두 클래스가 서로 긴밀한 관계에 있기 때문에 내부에 선언한 것이다.
내부 클래스를 사용하면 외부 클래스의 멤버들을 쉽게 접근할 수 있다.
이로 인해 코드의 복잡성이 감소하며 캡슐화를 할 수 있게 된다.
단, 내부 클래스는 외부 클래스를 제외하고 다른 클래스에서 잘 사용되지 않는 것이어야 한다.

익명 클래스(anonymous class)
내부 클래스에는 익명 클래스라는 것이 존재한다. 익명 클래스도 마찬가지로 이름 그대로의 역할을 수행한다. 클래스의 선언과 객체의 생성을 동시에 하는 이름없는 클래스로, 일회용 클래스인 것이다. 이름이 없기 때문에 생성자도 가질 수 없으며, 조상클래스의 이름이나 구현하고자 하는 인터페이스의 이름을 사용해서 정의하기 때문에, 하나의 클래스로 상속받는 동시에 인터페이스를 구현하거나 둘 이상의 인터페이스를 구현할 수 없다.


람다식(Lambda Expression)
Java는 두 번의 큰 변화를 겪게 되는데, JDK1.5부터 추가된 Generics 가 그 첫 번째이며, 두 번째는JDK1.8부터 추가된 람다식이다.
람다식의 도입으로 인해 자바는 객체지향언어인 동시에 함수형 언어가 되었다.

람다식이란 무엇인가
람다식은 간단히 말해서 메서드를 하나의 식으로 표현한 것이다.
메서드를 람다식으로 표현하게 되면 메서드의 이름과 반환값이 없어지므로 람다식을 익명 함수(anonymous function)이라고도 한다.
람다식의 문법은 다음과 같다.
메서드에서 이름과 반환타입을 제거하고 매개변수 선언부와 몸통 { } 사이에 ->를 추가해주는 것이다.
람다식은 statement(문장)이 아니고 식(expression)이기 때문에 세미콜론(;)을 붙이지 않는다.
1
Arrays.setAll(arr, () -> (int) (Math.random() * 5 + 1);
cs

이 람다식은 다음 코드와 같은 역할을 수행한다.
1
2
3
int method() {
     return (int)(Math.random() * 5 + 1);
}
cs


람다식은 메서드의 매개변수로 전달되어지는 것이 가능하고 메서드의 결과로 반환될 수도 있다. 매개변수의 타입마저 추론이 가능한 경우엔 생략이 가능하며, 반환타입도 항상 추론이 가능하기 때문에 생략한다.


자바에서 모든 메서드는 클래스 내에 포함되어야 한다.
그렇다면 람다식은 어떤 클래스에 포함되는 것일까?
메서드를 람다식으로 변경하면서 람다식의 문법을 설명하였지만.
람다식의 실체는 사실 익명 클래스의 객체와 동등한 입지를 갖는다.
1
2
3
4
5
6
7
8
9
10
@FunctionalInterface
interface MyFunction {
     public abstract max(int a, int b);
}
 
MuFunction f = new MyFunction() {
                              public int max(int a, int b) {
                                   return a > b ? a : b;
                              }
                         };
cs
조금 괴상망측하다. 위 코드는, 
1
MyFunction f = (int a, int b) -> a > b ? a : b;
cs
로 표현 가능하다.

람다식과 인터페이스의 메소드가 1:1로 연결되어야 하기 때문에, 함수형 인터페이스에는 오직 하나의 추상 메서드만 정의되어 있어야 한다.

자바에서는 객체지향적인 부분도 끌어안으며 함수형 프로그래밍의 패러다임을 끌어안아야 했다.
그래서 인터페이스를 통해 람다식을 다루기로 결정하였으며, 람다식을 다루기 위한 인터페이스를 함수형 인터페이스(functional interface)라고 부르기로 했다.
@FunctionalInterface를 붙이면 컴파일러가 함수형 인터페이스를 올바르게 정의하였는지 확인해주므로 붙이도록 하자.

람다식에서 외부함수에 접근할 수 있을까?
람다식도 익명 객체이기 때문에 외부에 선언된 변수에 접근하는 규칙은 익명 클래스에서의 규칙과 동일하다.
단, 람다식 내에서 참조하는 지역변수는 final 이 붙지 않았어도 상수로 간주된다.
람다식에서는 속한 클래스의 변수에 접근할 때 this 키워드를 통해 접근할 수 있으며,
한 단계를 더 넘어가 외부 클래스의 변수에 접근할 때는 클래스명과 this키워드를 함께 사용하여 접근한다.
1
2
3
4
5
6
7
8
9
10
11
12
class Outer {
     int val = 10// Outer.this.val = 10
 
     class Inner {
          int val = 20// this.val = 20
 
 
          void method(int i) { // 여기서 i는 상수가 된다!
               int val = 30// val = 30
          }
     }
}
cs


end