[Java] ☕ 람다식 (Lambda)
📖 람다식 (Lambda)
자바는 크게 2개의 커다란 변화가 있었다.
첫번쨰는 JDK 1.5
의 제네릭
의 등장
두번쨰는 JDK 1.8
의 람다식
의 등장이다.
프로그래밍은 크게 절차지향, 객체지향, 함수형이 있는데, 람다식
의 등장으로 인해서 자바는 객체지향 언어
임과 동시에 함수형 언어
가 되었다.
절차 지향 프로그래밍(Procedural Programming)
- 순차적인 처리를 중요시 여기며, 프로그램 전체가 유기적으로 연결되도록 만드는 프로그래밍 기법
객체지향 프로그래밍(Object Oriented Programming)
- 모든 데이터를 객체(Object)로 취급하여, 객체가 처리 요청을 받았을 때, 객체 내부에 있는 기능을 가져와 사용해 처리하는 기법
클래스가 일급 객체
- 유지보수가 뛰어나며 코드 재사용이 용의하다.
- 처리속도가 느리다.
함수형 프로그래밍(Functional Programming)
- 순수 함수를 사용해, 상테를 제어하기보단, 빠르게 처리하는데 초점을 둔 방법.
함수가 일급 객체
일급 객체란?
- 변수에 할당(assignment)할 수 있다.
- 다른 함수를 인자(argument)로 전달 받는다.
- 다른 함수의 결과로서 리턴될 수 있다.
람다식 이전에는 interface나 abstract를 사용하여 구현을 하였다.
특히, Arrays.sort()
를 사용하기 위해서 Comparator
를 사용하거나, 변수 자체에 Compare @Override
를 지원해야 하는데, 이를 람다식을 사용해서 단순하게 나타낼 수 있다.
📌 클래스 자체에 직접 선언
public class Book implements Comparable<Book> {
private int price;
public Book(int price){
this.price = price;
}
public int getPrice(){
return this.price;
}
@Override
public int compareTo(Book o) {
return this.price-o.price;
}
}
1번째는 class 자체에 비교연산이 가능하게 Comparable 이나 Comparator
를 사용하는 방식
📌 Arrays.sort에 Comparator 정렬 규칙 지정
Arrays.sort(books ,new Comparator<Book>() {
@Override
public int compare(Book o1, Book o2) {
return o1.getPrice()-o2.getPrice();
}
});
Arrays.sort
의 인자에 new Comparator
라는 정렬 규칙을 지정하는 방식
📌 Arrays.sort에 람다식을 지정
Collections.sort(books, (Book b1, Book b2)-> b1.getPrice()-b2.getPrice());
Arrays.sort
의 인자에 람디식을 지정하는 방식
🍄 람다식 사용법
(매개변수) -> { 원하는 처리내용 }
매개변수의 타입이나 반환타입 및 return
생략 또한 가능하다.
(int a, int b) -> { return a > b ? a : b }
(a, b) -> a > b ? a : b
📖 함수형 인터페이스
1개의 추상 메소드를 갖는 인터페이스이며 @FunctionalInterface
를 붙여준다.
@FunctionalInterface
interface FunctionalInterfaceClass {
// abstract method 오직 하나
int sum();
// default method 는 존재해도 상관없음
default void manual() {
System.out.println("더해주는 함수");
}
// static method 는 존재해도 상관없음
static void staticManual() {
System.out.println("더해주는 함수의 static");
}
}
public void main(){
FunctionalInterfaceClass<String> functionalInterfaceClass = (a, b) -> a + b ;
int result = functionalInterfaceClass.sum();
System.out.println(result);
functionalInterfaceClass.manual();
functionalInterfaceClass.staticManual();
}
📖 변수 캡처 (Variable Capture)
- 로컬 변수 캡처
- final이거나 effective final의 경우만 참조 가능
- 위를 만족 못하면
concurrency
문제가 발생하여 컴파일이 불가능하다.
- effective final
- 사실상 final인 함수
- 익명 클래스 구현체와 달리
쉐도윙
을 하지 않는다.
🍄 변수 캡처 예시
public class Foo {
public static void main(String[] args){
Foo foo = new Foo();
foo.run();
}
private void run() {
// 참조는 할 수 있다.
// java8 부터는 final 키워드를 생략할 수 있는 케이스가 있다.
// 이 변수가 사실상 final인 경우이다. (어디서도 이 변수를 변경하지 않는 경우)
int baseNumber = 10;
// 1. 로컬 클래스
class LocalClass{
void PrintBaseNumber(){
System.out.println(baseNumber);
}
}
// 2. 익명 클래스
Consumer<Integer> integerConsumer = new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(baseNumber);
}
};
// 3. 람다
IntConsumer printInt = (i) -> {
System.out.println(i + baseNumber);
};
printInt.accept(10);
}
}
🍄 로컬 클래스와 익명 클래스
-> 쉐도잉이 발생
int baseNumber = 10;
// 1. 로컬 클래스
class LocalClass{
void PrintBaseNumber(){
int baseNumber = 11;
// baseNumber 값은 11이 찍힐 것이다. (scope)
// run 메소드에서 선언한 baseNumber에 대해 쉐도잉이 발생
System.out.println(baseNumber);
}
}
// 2. 익명 클래스
Consumer<Integer> integerConsumer = new Consumer<Integer>() {
@Override
public void accept(Integer baseNumber) {
// 파라미터로 전달받은 baseNumber 가 찍힐 것이다.
// run 메소드에서 선언한 baseNumber에 대해 쉐도잉이 발생
System.out.println(baseNumber);
}
};
🍄 람다
-> 쉐도잉이 불가능
int baseNumber = 10;
// 3. 람다
IntConsumer printInt = (baseNumber) -> {
System.out.println(baseNumber);
};
- 위와 같이 선언하게 되면 에러가 발생 -> Variable ‘baseNumber’ is already defined in the scope
📖 메소드/생성자 레퍼런스
이미 익명클래스를 생성하는 것보다 짧은 람다를 더 줄이는 방법이 바로 메소드/생성자 레퍼런스
이다.
(String s) -> Integer.parseInt(s);
Integer::parseInt;
개인 공부 기록용 블로그입니다.
틀리거나 오류가 있을 경우 제보해주시면 감사하겠습니다.😁