오랜만에 포스팅이다. 👨💻
//익명클래스
Arrays.sort(words, new Comparator<String(){
public int compare(String s1,String s2){
return Integer.compare(s1.length(),s2.length());
}
});
//Lambda식
Arrays.sort(words, (s1,s2)-> Integer.compare(s1.length(),s2.length()));
함수형 인터페이스는 하나의 추상 메소드로 이루어진 메소드
@FunctionalInterface
를 선언하여 사용가능
컴파일러는 함수형 인터페이스에 정의된 정보를 참조하여 타입을 추론.
Arrays.sort(words, (s1,s2)-> Integer.compare(s1.length(),s2.length()));
선언방법
@FunctionalInteface
interface A {
void apply();
}
interface B extends A{
}
//또한 속성또한 이어받으므로 하나의 메소드 이상 추가 할 수 없음
// 하나의 추상메소드 외에 메소드 추가 불가
interface B extends A {
void illegal(); // error
}
@FunctionalInterface
interface Calculation {
Integer apply(Integer x, Integer y);
}
static Integer calculate(Calculation operation, Integer x, Integer y) {
return operation.apply(x, y);
}
// 람다 생성
Calculation addition = (x, y) -> x + y;
Calculation subtraction = (x, y) -> x - y;
// 사용
calculate(addition, 2, 2);
calculate(substraction, 5, calculate(addition, 3, 2));
//Lambda식
Arrays.sort(words, (s1,s2)-> Integer.compare(s1.length(),s2.length()));
//메소드 참조
Arrays.sort(words, Comparator.comparingInt(String::length));
메소드 참조의 유형은 총 5가지로 존재.
//정적 메소드 참조
Integer::parseInt
//같은 기능의 람다
str -> Integer.parseInt(str)
Instant.now()::isAfter
//같은 기능의 람다
Instance then = Instant.now();
t -> then.isAfter(t);
String::toLowCase
//같은 기능의 람다
str -> str.toLowerCase()
TreeMap<K,V>::new
//같은 기능의 람다
() -> new TreeMap<K,V>()
int[]::new
//같은 기능의 람다
len -> new int[len]
boolean
객체 리턴@FunctionalInterface
public interface Preidcate<T> { // T 형식을 받아 boolean 반환
boolean test(T t);
}
public boolean isGood(Predicate<Integer> func){
return func.test();
}
isGood((data)-> data%2 ==0);
void
객체 리턴@FunctionalInterface
public interface Consumer<T>{
void accept(T t);
}
public <T> void forEach(List<T> list, Consumer<T> c){
for(T t: list){
c.accept(t);
}
}
forEach(Arrays.asList(1,2,3,4,5), (Integer i) -> System.out.println(i));
java.util.function.Function
T 인수를 받아서 R 객체
를 리턴
@FunctionalInterface
public interface Function<T,R>{
R apply(T t);
}
public String toString(Function<Integer,String> func){
return func.apply();
}
String data = toString(it -> Integer::toString);
//딱히 생각나는 예제가 없어서...
이외에도...
등이 존재.
int,long,byte를 파라미터로 대입시 Wrapping 하는 비용이 발생
@FunctionalInterface
public interface Example {
public void test(Integer t);
}
public void hello(Example ex){
ex.test();
}
//Example.test가 Integer 를 Parameter로 받고 있기 때문에 a가 Integer형임을 알 수 있다.
hello( a -> System.out.println(a+1));
int portNumber = 8888;
Runnable r = () -> System.out.println(portNumber); //사용불가 -> 컴파일 단계에서 Error
portNumber =31337;
왜 이런 제약이 발생하냐면, 지역변수와 인스턴스 변수의 차이를 이해하면 알 수 있다.
지역변수는 스택에 위치하고, 인스턴스 변수는 힙에 위치한다. 람다가 지역변수에 바로 접근할 수 있는 가정하에 람다가 쓰레드에서 실행된다면 변수를 할당한 스레드가 사라져서 변수할당이 해제 되었는데도 람다를 실행하여, 스레드는 해당 변수에 접근할 수 있다.
따라서 자바 구현에서는 원래 변수에 접근을 허용하는 것이 아니라 지역 변수의 복사본을 제공한다.
그렇기 때문에 지역변수에는 한번만 값을 할당해야 된다 라는 제약이 생김.
디폴트 메소드
라고한다.Comparator<Apple> c = Comparator.comparing(Apple::getWeight);
inventory.sort(comparing(Apple::getWeight).reversed());
inventory.sort(comparing(Apple::getWeight)
.reversed()
.thenComparing(Apple.getCountry));
negate
,and
,or
세가지 디폴트 메소드를 제공한다. //기존 Predicate 객체의 결과를 반전
Preicate<Apple> notRedApple = redApple.negate();
//and 메소드를 이용해서, 빨간색이면서, 무거운 사과를 선택하도록 조합.
Preicate<Apple> redApple = redApple.and(apple -> apple.getWeight()> 150);
// 빨갛거나 초록색 사과 이면서 무거운 사과 조합
Predicate<Apple> redAndGreenApple = redApple
.and(apple -> apple.getWeight() > 150)
.or(apple -> GREEN.equals(apple.getColor()));