"자바 8 부터 나온 람다, 스트림 과 같은 함수형 프로그래밍의 기초적인 개념들을 정리하기 위한 글 입니다."
"더 자세한 내용은 '모던 자바 인 액션' 교재를 학습하며 포스팅 할 예정입니다."
@FunctionalInterface 어노테이션
코딩을 하다보면 람다식으로 구현한 인터페이스에 실수로 다른 메소드를 추가 할 수 있다.
그런 실수를 막기 위해 FunctionalInterface 어노테이션을 사용한다.
이 어노테이션을 사용하면 함수형 인터페이스 라는 의미이고, 이 어노테이션이 선언된 인터페이스에 메소드를 하나 이상 선언하면 오류가 발생하게 된다.
이 어노테이션을 반드시 사용해야 하는 것은 아니나, 함수형 인터페이스 라는 것을 명시적으로 표현함으로서 나중에 발생할 오류를 방지할 수 있게 된다.
객체 지향 프로그래밍 방식과 람다식 비교
- 람다식을 사용하면 기존 방식보다 간결한 코드를 구현할 수 있다.
- 메소드의 구현부를 클래스에 만들고, 이를 다시 인스턴스로 생성하고(객체 변수) 호출하는 코드가 줄어들기 때문이다.
* 문자열 두 개를 출력하는 예제의 경우
- 함수형 인터페이스 : StringConcat.java
@FunctionalInterface
public interface StringConcat {
// 문자열 두 개를 매개변수로 입력받아 두 문자열을 연결하여 출력하는 메소드
public void makeString(String s1, String s2);
// 이 인터페이스를 클래스와 람다식, 두 가지 방식으로 구현해보자.
}
- 객체 지향방식 구현시 인터페이스 구현 클래스 : StringConCatImpl.java
import LambdaInterface.StringConcat;
// 람다식을 활용하지 않을 경우 인터페이스에 선언된 메소드의 구현부를
// 따로 작성해줄 클래스를 만들어야 한다.
public class StringConcatImpl implements StringConcat{
// 함수형 인터페이스 내부에 선언된 메소드의 구현부
@Override
public void makeString(String s1, String s2) {
System.out.println(s1 + ", " + s2);
}
}
- 실행 클래스 : TestStringConcat.java
import LambdaInterface.StringConcat;
public class TestStringConcat {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = "World";
// 함수형 인터페이스를 객체지향 방식으로 구현한 경우
// 함수형 인터페이스를 구현하고 있는 클래스 객체를 만든 선언한 후 메소드를 호출한다.
StringConcatImpl concat1 = new StringConcatImpl();
concat1.makeString(s1, s2);
// 함수형 인터페이스를 람다식으로 구현한 경우
// 클래스를 따로 생성할 필요 없이 바로 메소드를 구현해서 사용할 수 있다.
StringConcat concat2 = (s,v) -> System.out.println(s + ", " + v);
concat2.makeString(s1, s2);
}
}
람다식은 익명 객체를 생성한다.
자바는 객체지향 언어임에도 불구하고 람다식은 객체 없이 인터페이스의 구현만으로 메소드를 호출할 수 있다.
자바는 객체 생성 없이 메소드 호출이 일어날 수 없는데 람다식의 경우 어떻게 메소드를 호출할 수 있는 것일까?
- 익명 내부 클래스는 클래스 이름 없이 인터페이스 자료형 변수에 바로 메소드 구현부를 생성하여 대입할 수 있다.
- 즉, 람다식으로 메소드를 구현해서 호출하면 컴퓨터 내부에서는 아래 처럼 익명 클래스가 생성되고, 이를 통해 익명 객체가 생성된다.
=> 익명 클래스 생성 및 객체 생성 예시 :
// StringConcat 클래스를 상속받는 익명 클래스 객체 생성
String concat3 = new StringConcat() {
@Override
public void makeString(String s1, String s2){
System.out.println(s1 + ", " + s2);
}
};
* 람다식에서 지역변수를 사용한다면 어떨까?
두 문자열을 연결하는 람다식 코드에서 외부 메소드의 지역변수인 i 를 수정하면 어떻게 될까?
- TestStringConcat.java
public class TestStringConcat{
public static void main(String[] args){
.....
int i = 100;
StringConcat concat2 = (s, v) -> {
i = 200; 람다식 내부에서 지역변수를 변경하면 오류가 발생한다.
System.out.println(i);
System.out.println(s + ", " + v);
};
}
}
위의 코드를 보면 람다식 내부에서 main() 함수의 지역변수 i 값을 변경하면 오류가 발생하는 것을 알 수 있다.
그런데 변경하지 않고 값을 출력하기만 하면 오류가 발생하지 않는다.
어째서일까?
지역변수는 메소드 호출이 끝나면 메모리에서 사라지기 때문에 익명 내부 클래스에서 사용하는 경우, 지역변수가 상수로 변한다.
람다식 역시 익명 내부 클래스가 생성되므로 외부 메소드의 지역변수를 사용하면, 해당 변수는 final 변수 즉, 상수가 된다.
따라서 이 변수를 변경하면 오류가 발생하는 것이다.
'JAVA > 람다,스트림' 카테고리의 다른 글
자바 람다식 기초 - 1 (0) | 2021.02.27 |
---|