https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html
//자바의 정석 코드
int[] arr = new int[5];
Arrays.setAll(arr, (i) -> (int) (Math.random() * 5) +1);
int methond(int i) {
return (int) (Math.random() * 5) +1);
}
(int a, int b) -> a > b ? a : b
// 사실 아래 익명 클래스 객체와 같다.
new Object() {
int max(int a, int b) {
return a > b ? a : b;
}
}
->
를 추가하면 된다. int max(int a, int b) {
//수행 내용
}
(int a, int b) -> {
//수행 내용
}
;
를 붙이지 않는다. int add(int a, int b) {
return a + b;
}
(int a, int b) -> a + b
(int a, int b) -> {
return a+b;
}
//생략 전
(int a, int b) -> a > b ? a : b
//생략 후
(a, b) -> a > b ? a : b
//생략 전
(a) -> a * 2
//생략 후
a -> a * 2
//() 생략 불가
(int a) -> a * 2
{ }
안에 문장이 한 개라면 생각 가능, return문일 경우 생략 불가 (int a, int b) -> {a+b;}
//생략 후 ( ; 를 붙이지 않기!)
(int a, int b) -> a + b
(int a, int b) -> {return a+b;}
//에러. 불가한 코드.
(int a, int b) -> return a+b;
위에서 람다식은 사실 익명 클래스의 객체라고 했다. 이 친구를 받아줄 때 타입과 변수로 받아줘야 할 것이다. 이 때 필요한 것이 함수형 인터페이스
https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html#approach6
interface CheckPerson {
boolean test(Person p);
}
interface Predicate<T> {
boolean test(T t);
}
Predicate<T>
를 호출하면 아래와 같이 오버라이딩 하라고 자동 완성된다 new Predicate<Integer>
한 부분이 인터페이스를 구현한 익명 클래스의 객체를 생성한 것이다. https://www.baeldung.com/java-8-functional-interfaces
@FunctionalInterface
을 사용한다. variable capture
이다. final 이고 final 처럼 사용해야 되기 때문에 위에서 말한 것처럼 람다식 내부에서 지역변수를 변경하지 못한다. package bongf.week15.study;
import java.util.Arrays;
public class VariableCapture {
private static int x = 2;
private int y = 1;
public static void main(String[] args) {
AInterface add = i -> i + x; // static 변수 접근 가능
AInterface changeStaticVariable = i -> {
x++;
return i + x;
};
int a = 1;
for (int i = 0; i < 5; i++) {
a++;
}
int[] test = new int[]{1, 2, 3};
// Arrays.stream(test).map(x -> x + a).getAsInt(); //에러. final이 아니다.
}
public void accessInstanceVariable() {
AInterface subtract = i -> i - y; // 인스턴스 변수 접근 가능
AInterface subtract2 = i -> {
y--; // 지역변수가 아니기 때문에 변경 가능하다.
return i - y; // 인스턴스 변수 접근 가능
};
}
public static void accessLocalVariable() {
int z = 1;
AInterface divde = i -> i / z; // 로컬변수 접근 가능
AInterface notFianl = (int x) -> {
// z = z*z; //에러 effectively final이기 때문에 변경할 수 없다.
return z;
};
}
}
@FunctionalInterface
interface AInterface {
int operate(int x);
}
package bongf.week15.study;
import java.util.function.Consumer;
public class LambdaScopeTest {
public int x = 0;
class FirstLevel {
public int x = 1;
void methodInFirstLevel(int x) {
int z = 2;
// 여기서 y대신 x나, z로 바꾸면 에러 난다.
// "Lambda expression's parameter x cannot redeclare another local variable defined in an enclosing scope"
// 외부함수의 범위(enclosing scope)에서 정의된 지역 변수 x를 재 정의 할 수 없다.
// 람다는 람다를 감싸고 있는 메소드와 같은 스코프이기 때문에
// 이미 z나 x를 정의한 것으로 인식한다.
// 만약 이를 익명 클래스로 작성하면 에러가 발생하지 않는다.(아래 testShadowing) 익명 클래스는 쉐도잉 이슈가 있지만 람다는 없다.
// 자세한 내용은 https://www.notion.so/758e363f9fb04872a604999f8af6a1ae
Consumer<Integer> myConsumer = (y) ->
{
// The following statement causes the compiler to generate
// the error "Local variable z defined in an enclosing scope
// must be final or effectively final"
//
// z = 99;
System.out.println("x = " + x); // 23
System.out.println("y = " + y); // 23 매개변수로 넣어준
System.out.println("z = " + z); // 2
System.out.println("this.x = " + this.x); // 1
System.out.println("LambdaScopeTest.this.x = " +
LambdaScopeTest.this.x); //0
};
myConsumer.accept(x);
// 익명 클래스로 작성하면 'z'라고 써도 문제가 없다.
Consumer<Integer> testShadowing = new Consumer<Integer>() {
@Override
public void accept(Integer z) {
System.out.println(z);
}
};
}
}
public static void main(String... args) {
LambdaScopeTest st = new LambdaScopeTest();
LambdaScopeTest.FirstLevel fl = st.new FirstLevel();
fl.methodInFirstLevel(23);
}
}
클래스이름::메서드이름
혹은참조변수::메서드이름
으로 바꿀 수 있다.(x) -> ClassName.method(x)
(obj, x) -> obj.method(x)
(x) -> obj.method(x)
//메서드 참조 후
ClassName::method //클래스의 static 메서드 참조
ClassName::method //인스턴스 메서드 참조
obj::method //특정 객체의 인스턴스 메서드 참조
() -> new AClass();
() -> AClass::new