람다식
람다식이란?
- 수학자 알론조 처치가 람다 계산법에서 사용된 식으로 제자인 존 매카시가 프로그래밍 언어에 도입
- 익명 함수를 생성하기 위한 식으로
함수 지향 언어
에 가깝다. - 람다식의 형태는 매개 변수를 가진 코드 블록이지만, 런타임 시에는 익명 구현 객체를 생성한다.
람다식
Runable runable = () -> {...};
익명 구현 객체
Runable runable = new Runable() {
public void run() {...}
}
함수 지향 언어란? 자료 처리를 수학적 함수의 계산으로 취급하고 상태와 가변 데이터를 멀리하는 프로그래밍 패러다임의 한 종류
람다식 기본 문법
(타입, 매개 변수) -> {실행문}으로 괄호안에 있는 매개 변수를 이용해서 중괄호를 실행한다는 뜻이다.
// 기본 형태: (타입 매개 변수) -> {실행문}
(int a) -> {System.out.println(a);};
// 매개 변수 타입은 일반적으로 언급 하지 않음: (매개 변수) -> {실행문}
(a) -> {System.out.println(a);};
// 매개 변수나 실행문이 한개라면 괄호 생략 가능: 매개 변수 -> 실행문
a -> System.out.println;
// 매개 변수가 없다면 괄호 필수: () -> {실행문}
() -> {System.our.pirntln("hello world");};
//return문 사용 가능
(x,y) -> {return x+y;};
//중괄호 안에 return문만 있다면 return과 괄호 생략 가능
(x,y) -> x + y;
타겟 타입
람다식은 메소드를 선언하는 것이 아니라 이 메소드를 가지고 있는 객체를 생성한다.
인터페이스 변수 = 람다식;
람다식은 인터페이스 변수에 대입되기 때문에 인터페이스의 익명 구현 객체를 생성한다. 대입될 인터페이스의 종류에 따라 작성 방법이 달라지기 때문에 람다식이 대입될 인터페이스를 람다식의 타겟 타입이라고 부른다.
함수적 인터페이스
하나의 추상 메소드가 선언된 인터페이스를 함수적 인터페이스라고 부른다.
@FunctionalInterface 어노테이션을 사용하면 두 개 이상의 추상 메소드가 있는지 체크해준다.
@FunctionalInterface
public interface MyFunctionalInterface(){
public void method1();
public void method2(); // 컴파일 오류
}
람다식 예제
인터페이스
@FunctionalInterface
public interface MyFunctionalInterface(){
public void method1(int x, int y);
}
메인 메소드
MyFunctionalInterface fi;
fi = (int x, int y) -> {
int sum = x + y;
System.out.println(sum);
};
fi.method(2,3); // 5
클래스 멤버 사용
람다식 실행 블록에는 클래스의 멤버인 필드와 메소드를 제약 사항 없이 사용할 수 있다.
this 키워드를 사용할때는 주의가 필요한데 람다식에서 this는 람다식을 실행한 객체의 참조이다.
public class UsingThis {
public int outterField = 10;
class Inner {
int innerField = 20;
void method(){
MyFunctionalInterface fi = () -> {
System.out.println(UsingThis.this.outterField); // 10
System.out.println(this.innerField); // Inner 클래스가 람다식을 실행한 객체
}
}
}
}
로컬 변수 사용
람다식은 메소드 내부에서 주로 작성되기 떄문에 로컬 익명 구현 객체를 생성시킨다.
메소드의 매개 변수 또는 로컬 변수를 사용하고싶다면 이 두 변수는 final 특성을 가져야 한다. 따라서 람다식 내부 또는 외부에서 변경할 수 없다.
익명 객체의 로컬 변수 사용에서 final 특성을 가져야하는 이유:
매개 변수나 로컬 변수는 메소드 실행이 끝나면 메모리에서 사라지기 때문에 익명 객체에서 사용하지 못한다. final 특성을 가진 변수는JVM Constant pool
에 따로 보관이 된다. 따라서 메소드가 가비지 컬렉션에 반납되어도 final 변수는 사용 가능하다.
표준 API의 함수적 인터페이스
메소드 또는 생성자의 매개 타입으로 사용되어 람다식을 대입할 수 있도록 java.util.function 표준 API 패키지를 제공한다.
종류 | 추상 메소드 특징 |
---|---|
Consumer | -매개값⭕️, 리턴값❌ |
Supplier | -매개값⭕️, 리턴값⭕️ |
Function | -매개값⭕️, - 리턴값⭕️ -주로 매개값을 리턴값으로 매핑 |
Operator | -매개값⭕️, 리턴값⭕️ -주로 매개값을 연산하고 결과를 리턴 |
Predicate | -매개값⭕️, 리턴값 boolean -매개값을 조사해서 true/false 리턴 |
Consumer 함수적 인터페이스
Consumer 함수적 인터페이스는 리턴값이 없는 accpet()
메소드를 가지고 있다.
예제
Consumer<String> consumer = t -> System.out.println(t + "8");
consumer.accept("java"); // java8
BiConsumer<String> bigConsumer = (t, u) -> System.out.printlne(t + 8);
bigconsumer.accpet("java","8"); // java8
DoubleConsumer doubleConsumer = d -> System.out.printlne("java" + d);
doubleConsumer.accpet(8.0); // java8.0
Supplier 함수적 인터페이스
Supplier 함수적 인터페이스는 매개 변수가 없고 리턴값이 있는 getXXX()
를 가지고 있다.
예제
Supplier<String> supplier = () -> {
return "hello";
};
System.out.println(supplier.get()); // hello
IntSupplier intSupplier = () -> {
return 4;
};
int result = 2 + intSupplier.getAsInt();
System.out.println(result) // 2 + 4 = 6;
Functional 함수적 인터페이스
Functional 함수적 인터페이스는 매개 변수와 리턴 값이 있는 applyXXX()
를 가지고 있다.
매개값을 리턴값으로 매핑
예제
private static List<Student> list = Arrays.asList(
new Student("홍길동", 90, 96), //name, mathSore, englishScore
new Student("신용권",92, 99)
);
public static void printString(Function<Student, String> function) {
for (Student student : list) {
System.out.println(function.apply(studnet) + " ");
}
System.out.println();
}
public static double avg(ToIntFucntion<Studnet> function) {
int sum = 0;
for(Student student : list){
sum += function.applyAsInt(student);
}
return sum / list.size();
}
public static void main(String[] args) {
printString(t -> t.getName());// 홍길동, 신용권
System.out.println(avg(t -> t.getMathScore())); // (90 + 92) / 2
}
Operator 함수적 인터페이스
Operator 함수적 인터페이스는 Function과 동일하게 매개 변수와 리턴 값이 있는 applyXXX()
를 가지고 있다.
매개 값을 이용하여 연산후 동일한 타입으로 리턴 값으 제공
예제
private static int[] scores = {92, 95, 87 };
public static int maxOrMin(IntBinaryOperator operator) {
int result = scores[0];
for(int score : scores) {
result = operator.applyAsInt(result, socre);
}
return result;
}
public static void main(String[] args) {
int max = maxOrMin(
(a,b) -> {
if (a>=b)
return a;
else
return b;
}
);
System.out.println(max); // 95
}
Predicate 함수적 인터페이스
Predicate 함수적 인터페이스는 매개 변수와 boolean 리턴 값이 있는 testXXX()
를 가지고 있다.
매개 값을 조사해서 true or false를 리턴한다.
예제
private static List<Student> list = Arrays.asList(
new Student("홍길동", "남자", 80),
new Student("김순희", "여자", 93),
new Student("김자바", "여자", 87),
new Student("박한다", "남자", 94)
);
public static double avg(Predicate<Student> predicate) {
int count = 0, sum = 0;
for(Student student : list) {
if(predicate.test(student)) {
count++;
sum += student.getScore();
}
}
return (double) sum / count;
}
public static void main(String[] args) {
System.out.println(avg(t -> t.getSex().equals("남자"))); // (80 + 87) / 2
}
메소드 참조
메소드를 참조하여 매개 변수의 정보 및 리턴 타입을 알아내어 불필요한 매개 변수를 제거한다.
정적 메소드와 인스턴스 메소드 참조
클래스 :: 메소드
참조 변수 :: 메소드
(a, b) -> Class.method(a, b);
Class :: medthod
매개 변수의 메소드 참조
(a, b) -> a.instanceMethod(b);
클래스 :: instanceMethod;
생성자 참조
(a, b) -> {return new 클래스(a, b};
클래스 :: new
생성자가 여러개 있을 경우, 컴파일러는 함수적 인터페이스의 추상 메소드와 동일한 매개 변수 타입과 개수를 가지고 있는 생성자를 찾아 실행
해당 생성자가 없으면 컴파일 오류