| 계열 | 입력 | 출력 | 메소드 | 설명 | 
|---|---|---|---|---|
| Consumer | O | X | void accept(T) | 입력을 소비 | 
| Supplier | X | O | T get() | 출력을 공급 | 
| Function | O | O | T apply(R) | 입력 -> 출력 함수 매핑 | 
| Operation | O | O | T apply(T) | 입력을 연산하여 동일 타입의 출력으로 리턴 | 
| Predicate | O | boolean | boolean test(T) | 입력을 판단 | 
| 인터페이스 | 메소드 | 
|---|---|
| Consumer | void accept(T t) | 
| BiConsumer<T, U> | void accept(T t, U u) | 
| IntConsumer | void accept(int value) | 
| LongConsumer | void accept(long value) | 
| DoubleConsumer | void accept(double value) | 
| ObjIntConsumer | void accept(T t, int value) | 
| ObjLongConsumer | void accept(T t, long value) | 
| ObjDoubleConsumer | void accept(T t, double value) | 
import java.util.function.*;
public class Main {
    public static void main(String[] args) {
        Consumer<String> consumer = (s)-> System.out.println(s);
        // <String>이므로 매개값 s는 String
        consumer.accept("A String.");
        BiConsumer<String, String> biConsumer = 
                         (t,u) -> System.out.println(t+","+u);
        // <String,String> 이므로 매개값 t와 u는 모두 String 타입
        biConsumer.accept("Hello","world");
        // 오토박싱/ 언방식 사용하면 비효율적이다.
        Consumer<Integer> integerConsumer = (x) -> System.out.println(x);
        integerConsumer.accept(10); // 값이 들어갈 땐 오토박싱 출력할 때 언박싱
        // 효율적으로 하기 위해서 IntConsumer 제네릭이 아니다 기본형 타입
        // 기본형 입력을 하려고 할 경우, PConsumber (p: primitive type)을 사용 가능.
        // 주의! 오버로딩이 아니고 별도의 인터페이스이다. 
        // 최적화를 위해서 불편하더라도 별도로 만들어 놓은 것이다.
        IntConsumer intConsumer = (x) -> System.out.println(x);
        intConsumer.accept(5); 
        // 객체가 아니라 값을 입력을 받는 것이다. 기본자료형이니깐
        //LongConsumer, DoubleConsumer
        // t는 <>안에 값 x는 objIntconsumber의 int의 자료형이 들어간다.
        ObjIntConsumer<String> objIntConsumer = 
                       (t,x) -> System.out.println(t + ": "+ x);
        objIntConsumer.accept("x",1024);
        // ObjLongConsumer,ObjDoubleConsumer
        // 총 4가지 타입이 있다.
    }
}
| 인터페이스 | 메소드 | 
|---|---|
| Supplier | T get() | 
| BooleanSupplier | boolean getAsBoolean() | 
| IntSupplier | int getAsInt() | 
| LongSupplier | long getAsLong() | 
| DoubleSupplier | double getAsDouble() | 
import java.util.function.BooleanSupplier;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
public class Main {
    public static void main(String[] args) {
        Supplier<String> supplier = () -> "A String";
        // 입력을 받지 않기때문에 ()이 필요하다.
  
        System.out.println(supplier.get()); 
        // get()을 해서 출력을 한다.
        // BiSupperlier는 입력은 여러 개 할 수 있지만, 
        // 출력은 하나 밖에 못하기 때문에 없다.
        //Supplier는 P Type 계열에서 getAsP 메소드로 정의가 된다. primitive
        // 메소드가 다르다. getAsInt()...
        BooleanSupplier boolsup = () -> true;
        System.out.println(boolsup.getAsBoolean()); 
        // 이것은 getAsBoolean()으로 출력한다.
        // IntSupplier, LongSupplier, DoubleSupplier
        IntSupplier rollDice = () -> (int)(Math.random() * 6);
        //0~6까지 나와서 6은 나오지 않음 0~5까지만 실제 값이 나온다.
        for (int i = 0; i < 10; i++) {
            System.out.println(rollDice.getAsInt());
        }
        int x = 4;
        IntSupplier intSupp = () -> x; //로컬변수에도 접근할 수 있다.
        // 람다식을 활용할 때 모든 변수에 접근하여 활용할 수 있다.
        // 고정되어있는 값뿐만아니라 동적으로도 주변 값들을 공급할 수 있다.
        // 그래서 supplier가 나름대로의 의미가 있다??
        System.out.println(intSupp.getAsInt());
    }
}
| 인터페이스 | 메소드 | 
|---|---|
| Function<T, R> | R apply(T t) | 
| BiConsumer<T, U, R> | R apply(T t, U u) | 
| PFunction | R apply(p value) | 
| PtoQFunction | q applyAsQ(p value) | 
| ToPFunction | p applyAsP(T t) | 
| ToPBiFunction<T, U> | p applyAsP(T t, U u) | 
P, Q 는 기본 자료형(Primitive Type) : Int, Long, Double
p, q 는 기본 자료형(Primitive Type) : int, long, double
Funtion<Student, String> funtion = t-> {return t.getName()};
ToIntFuntion funtion = t -> {return t.getScore();}
import java.util.function.*;
public class Main {
    public static void main(String[] args) {
        Function<String,Integer> func = (s) -> s.length();
        // s 는 String타입, s.length() 는 Integer
        System.out.println(func.apply("Strings")); //이것은 apply로 출력한다
        // Bi가 붙으면 '입력'을 2개 받을 수 있다는 의미이다.
        BiFunction<String,String,Integer> biFunction = (s,u) -> s.length() + u.length();
        System.out.println(biFunction.apply("one","two")); //6
        // IntFunction<R>은 리턴 자료형
        // P type Funtion은 입력을 P타입으로 받는다.
        IntFunction<String> intFunction = (value) -> String.valueOf(value);// "" + value도 가능.
        System.out.println(intFunction.apply(512));
        //ToP Type Function은 출력을 P타입으로 한다.
        ToIntFunction<String> funcFour = (s) -> s.length(); // 4:21
        System.out.println(funcFour.applyAsInt("abcde"));
        // 출력이 P타입인 경우에는 AsP가 들어간다.!!!
        //ToIntBiFunction<String,String>// int 출력을 하는 Bi 함수
        // P: Int, Long, Double
        // int 에서 double로 바꾸는 함수 PToQFunction : P -> Q로 매핑하는 함수
        IntToDoubleFunction funcfive;
        // IntToIntFunction은 없다. 동일한 것에 대해서는 다른게 있다.
    }
}
| 인터페이스 | 메소드 | 
|---|---|
| UnaryOperator | T apply(T t) | 
| BinaryOperator | T apply(T t1, T t2) | 
| IntUnaryOperator | int applyAsInt(int value) | 
| LongUnaryOperator | long applyAsLong(long value) | 
| DoubleUnaryOperator | double applyAsDouble(double value) | 
| IntBinaryOperator | int applyAsInt(int value1, int value2) | 
| LongBinaryOperator | long applyAsLong(long value, long value2) | 
| DoubleBinaryOperator | double applyAsDouble(double value, double value2) | 
import java.util.function.BinaryOperator;
import java.util.function.IntBinaryOperator;
import java.util.function.IntUnaryOperator;
import java.util.function.UnaryOperator;
public class Main {
    public static void main(String[] args) {
        // 그냥 operator는 없다.
        // 입력이 1개 인 것을 Unary를 붙여서 표현
        UnaryOperator<String> operator = s -> s+"."; 
        // 리턴타입을 따로 입력받지 않는다 입출력이 같으니깐
        System.out.println(operator.apply("왔다")); // apply() 사용.
        BinaryOperator<String> operator1 = (s1,s2) -> s1 + s2;
        // 타입은 하나만 입력받게 되어있다. 출력은 동일한 타입이여야 하니깐?
        System.out.println(operator1.apply("나","왔다"));
        IntUnaryOperator op = value -> value*10; 
        //타입을 받지 않는다 어차피 int입력 int출력이니
        System.out.println(op.applyAsInt(5));
        // LongUnaryOperator, DoubleUnaryOperator
        IntBinaryOperator ibo = (v1,v2) -> v1 * v2;
        System.out.println(ibo.applyAsInt(10,20));
        //LongBinaryOperator, DoubleBinaryOperator
    }
}
| 인터페이스 | 메소드 | 
|---|---|
| Predicate | boolean test(T t) | 
| BiPredicate<T, U> | boolean test(T t, U u) | 
| IntPredicate | boolean test(int value) | 
| LongPredicate | boolean test(long value) | 
| DoublePredicate | boolean test(double value) | 
import java.util.function.BiPredicate;
import java.util.function.IntPredicate;
import java.util.function.Predicate;
public class Main {
    public static void main(String[] args) {
        Predicate<String> predicate = (s) -> s.length() == 4; 
        // 조건식이 들어가야 한다.
        System.out.println(predicate.test("Four")); 
        // test()를 사용한다 true or false 값 출력
        System.out.println(predicate.test("six"));
        BiPredicate<String, Integer> pred2 = (s,v) -> s.length() ==v;
        System.out.println(pred2.test("abcd",23));
        System.out.println(pred2.test("abc",3));
        IntPredicate pred3 = x -> x > 0;
        //LongPredicate, DoublePredicate asP출력은 존재하지 않는다.
    }
}
두 개 이상의 함수형 인터페이스를 연결하기 위해서 사용한다.
A.andThen(B) : A를 먼저 실행하고 B를 실행, Consumer, Function, Operator 계열의 default method로 구현 Consumer<String> c0 = s -> System.out.println("c0 :" + s);
 Consumer<String> c1 = s -> System.out.println("c1 :" + s);
 Consumer<String> c2 = c0.andThen(c1);
 c2.accept("String"); //동일한 스트링을 출력한다.
A.compose(B) : B를 먼저 실행하고 A를 실행, Function, Operator 계열의 default method로 구현 // Function 계열은 입력->출력 ==> 입력-> 출력 타입이 연쇄 되어야 한다.
 Function<String, Integer> func1 = s -> s.length();
 Function<Integer, Long> func2 = value -> (long)value;
 // func1의 입력이 String, 출력이 Integer이니깐, andThen()의 입력은 Integer여야 한다.
 Function<String,Long> func3 = func1.andThen(func2);
 System.out.println(func3.apply("four"));
 
 Function<String, Long> func4 = func2.compose(func1);
 System.out.println(func4.apply("four"));
Predicate 계열의 기본 메소드
 // 객체에서 메소드로 접근한다.
 DoublePredicate p0 = x -> x > 0.5;
 DoublePredicate p1 = x -> x < 0.7;
 DoublePredicate p2 = p0.and(p1);
 DoublePredicate p3 = p0.or(p1); 
 DoublePredicate p4 = p0.negate();
 System.out.println(p0.test(0.9)); //true
 System.out.println(p1.test(0.9)); // false
 System.out.println(p2.test(0.9)); // false
 System.out.println(p3.test(0.9)); // true
 System.out.println(p4.test(0.9)); // false not p0
Predicate 계열의 클래스 메소드
 Predicate<String> eq = Predicate.isEqual("String");
 // 함수형 인터페이스를 사용할 수 있다. 람다식 사용 x
 // 들어오는 String이랑 eq랑 같은지 테스트해주는 함수형 인터페이스를 리턴해줌
 System.out.println(eq.test("String")); // true
 System.out.println(eq.test("String!")); // false
public class Main {
    public static void main(String[] args) {
        BinaryOperator<String> minBy = 
            BinaryOperator.minBy((o1,o2)-> o1.length() > o2.length() ? 1 : -1);
        BinaryOperator<String> maxBy = 
            BinaryOperator.maxBy((o1,o2)-> o1.length() > o2.length() ? 1 : -1);
          
     // BinaryOperator.minBy((String o1,String o2)-> o1.length()+o2.length()); 
     // 이것도 가능
     // 어떤걸 받아 줄건지 써줘야 한다.
     // 앞에 String 타입을 넣어주면 뒤에서 o1,o2가 String 이라는 것을 추론할 수 있다.
  
          System.out.println(minBy.apply("abc","cd")); // 더 작은게 출력됨
          System.out.println(maxBy.apply("abc","cd")); // 더 큰게 출력됨
          System.out.println(minBy.apply("abc","cde")); // abc가 출려됨
      }
  }
메소드를 참조해서 매개변수의 정보 및 리턴타입을 알아내어
람다식에서 불필요한 매개변수를 제거하는 것이 목적이다.
람다식에 기존에 구현되어 있는 내용을 재사용하고자 할 때 사용
종종 람다식은 기존 메소드를 단순하게 호출만 하는 경우가 있다.
(left, right) -> Math.max(left,right);   ==> Math::max (메소드 참조)
메소드 참조도 람다식과 마찬가지로 인터페이스의 익명 구현 객체로 생성이 된다.
String[] strings = { "A", "B", "D", "C" };
Arrays.sort(strings, String::compareTo);
Function<String, Integer> parser = Integer::parseInt;
String string = "StringA";
Predicate<String> pred = string::equals;
System.out.println(pred.apply("StringA"));
public class Calculator {
    public static int staticMethod(int x, int y){
        return x+y;
    }
    public int instanceMethod(int x, int y){
        return x+y;
    }
}
public class MethodReferencesExam {
    public static void main(String[] args) {
        IntBinaryOperator operator;
        // 정적 메소드 참조
        operator = (x,y) -> Calculator.staticMethod(x,y);
        System.out.println("결과1:"+operator.applyAsInt(1,2));
        operator = Calculator::staticMethod;
        System.out.println("결과2:"+operator.applyAsInt(3,4));
        // 인스턴스 메소드 참조
        Calculator obj = new Calculator();
        operator = (x,y) -> obj.instanceMethod(x,y);
        System.out.println("결과3:"+operator.applyAsInt(5,6));
        operator = obj::instanceMethod;
        System.out.println("결과4:"+operator.applyAsInt(7,8));
    }
}
 public class ArgumentMethodReferencesExam {
     public static void main(String[] args) {
         ToIntBiFunction<String,String> function;
 
         function = (a,b) -> a.compareToIgnoreCase(b);
         print(function.applyAsInt("java8","JAVA8"));
         function = String::compareToIgnoreCase;
         print(function.applyAsInt("kplus","JAVA8"));
 
     }
 
     public static void print(int order) {
         if (order < 0){
             System.out.println("사전순으로 나옵니다.");
         }else if(order == 0){
             System.out.println("동일한 문자열 입니다.");
         }else{
             System.out.println("사전역순으로 나옵니다.");
         }
     }
 }                         IntFunction<String> func = String::new;
String string = func.apply(10);
public class Member {
    private String name;
    private String id;
    public Member() {
        System.out.println("Member() 실행");
    }
    public Member(String id) {
        System.out.println("Member(String id) 실행");
        this.id = id;
    }
    public Member(String name, String id) {
        System.out.println("Member(String name, String id) 실행");
        this.name = name;
        this.id = id;
    }
}
public class ConstructorReferencesExam {
    public static void main(String[] args) {
        Function<String,Member> function1= Member::new;
        Member member1 = function1.apply("angel");
        BiFunction<String,String,Member> function2 = Member::new;
        Member member2 = function2.apply("홍길동","hong");
      // 매개변수가 어떻게 제공이 되는야에 따라서 어떤생성자가 생성이되는지 달라진다.
    }
}
IntFunction<String[]> func = String[]::new;
String [] strings = func.apply(100);
이펙티브 자바 책 읽으면서 이해가 잘 안갔는데, 도움이 많이 되었습니다.! 감사합니다😄