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