자바 8 람다와 인터페이스 스펙 변화

zio도미닉·2021년 11월 17일
0

람다?

  • 코드 블록
  • 기존의 코드 블록은 메소드를 -> 메소드를 사용하기 위해 익명 객체를 만들거나 하는 식
  • 하지만 자바 8부터는 람다를 이용하여 코드 블록 생성 가능
  • 코드 블록을 변수처럼 사용 가능!

함수형 인터페이스를 람다식으로 변경

  • 함수형 인터페이스란?
    - 추상 메소드를 하나만 갖는 인터페이스
    - ex) Runnable 인터페이스 -> run() 추상 메소드 하나만 갖음
  • 기존 방식의 코드 블록 사용 : 별도의 클래스와 객체 그리고 메소드를 생성해야 한다.
  • 기존의 코드 방법 : 객체 생성 -> 이용
package book;

public class Test1 {

    public static void main(String[] args) {
        MyTest mt=new MyTest();
        Runnable r=mt;
        r.run();

    }

}

class MyTest implements Runnable {

    @Override
    public void run() {
        System.out.println("Hello Lambda");
    }
}
  • 익명 객체 생성하고 사용
package book;

public class Test2 {

    public static void main(String[] args) {
        
        // 익명 객체 생성
        Runnable r =new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello Lambda 2");
            }
        };

        r.run();

    }
}
  • 익명 객체 없이 람다를 이용하여 코드 블록만 사용
    - 가능한 이유?!
    • Runnable 타입으로 참조 변수 r을 만들고 있기 때문에 굳이 new Runnable()을 사용할 필요 없음.
    • run ( ) 메소드 -> ( )로 변경 : 이유는 Runnable 인터페이스가 가진 추상 메소드가 run () 메소드 하나 이기 때문에
package book;

public class Test3 {

    public static void main(String[] args) {

        Runnable r=() -> {
            System.out.println("Hello lambda 3");
        };

        r.run();

    }
}
  • 더 짧게?! 코드 안에 들어가는 로직이 1줄이기 때문에 { } 생략가능
package book.chapter01.interfaceFunction;

public class Test4 {
    public static void main(String[] args) {

        Runnable r =()-> System.out.println("lambda 4");
        r.run();
        
    }

}

람다 구조

  • 람다의 구조 : (인자 목록) -> {로직}

나만의 인터페이스를 이용해서 람다로 변경

package book.chapter02.myInterfaceFunction;

public class Test1 {

    public static void main(String[] args) {

        MyFunctionInterface m1=(int a) -> {return a*a;};

        int b=m1.runSomething(5);
        System.out.println(b);

    }
}

@FunctionalInterface
interface MyFunctionInterface {
    public abstract int runSomething(int count);
}
  • Main 함수에 부분을 더 줄일수 있을까?!
    - int가 들어가는 것을 알기 때문에 (인터페이스에서) 따라서 int 생략 가능
    • 인자 1개가 필요함으로 a를 쓴다!
    • runSomething부분에 인자가 int인것을 알기 때문에 return 부분 생략 가능
    • 코드가 1줄이기 때문에 { } 생략 가능
package book.chapter02.myInterfaceFunction;

public class Test2 {

    public static void main(String[] args) {
        MyFunctionInterface mf1=a->a*a;
        int b=mf1.runSomething(6);
        System.out.println(b);

    }
}

메소드 호출 인자 & Return 람다 사용

  • 지금까지는 람다식을 함수형 인터페이스의 참조 변수에 저장해서 사용
  • 람다식을 변수에 저장하는 것이 가능하다면 당연히 메서드의 인자로도 사용 가능
package book.chapter02.myInterfaceFunction;

public class Test3 {

    public static void main(String[] args) {
        MyFunctionInterface mf1=a->a*a;
        
        // 메소드에 인자값으로 넣어 사용
        doIt(mf1);
    }

    public static void doIt(MyFunctionInterface mf1) {
        int b=mf1.runSomething(5);
        System.out.println(b);
    }
}
  • 람다식을 1번만 사용한다면 참조변수를 만들 필요도 없다!
package book.chapter02.myInterfaceFunction;

public class Test4 {
    public static void main(String[] args) {
        doIt(a->a*a);
        
    }


    public static void doIt(MyFunctionInterface mf1) {
        int b=mf1.runSomething(5);
        System.out.println(b);
    }
}
  • 메소드의 반환값으로 람다 사용
package book.chapter02.myInterfaceFunction;

public class Test5 {
    public static void main(String[] args) {
        MyFunctionInterface mf1=todo();
        int result=mf1.runSomething(5);
        System.out.println(result);
    }
    
    public static MyFunctionInterface todo() {
        return num->num*num;
    }
}

자바 8 API에서 제공하는 대표적인 함수형 인터페이스

함수형 인터페이스추상 메소드용도
Runnablevoid run()실행할 수 있는 인터페이스
Supplier<T>T get()제공할 수 있는 인터페이스
Consumervoid accepnt(T t)소비(출력)할수 있는 인터페이스
Predeicate<T>Boolean test(T t)입력을 받아 참/거짓을 단정 인터페이스
UnaryOperator<T>T apply(T t)단항(Unary)연산을 할 수 있는 인터페이스
package book.chapter03.java8FunctionInterface;

import java.util.Arrays;
import java.util.List;
import java.util.function.*;

public class Test1 {
    public static void main(String[] args) {

        // 실행할수 있는 인터페이스
        Runnable run=()-> System.out.println("hello");
        run.run();

        // 1. 제공할 수 있는 인터페이스
        Supplier<Integer>sup=()->3*3;
        int m=sup.get();
        System.out.println(m);

        // 2. 소비할 수 있는 인터페이스 (출력)
        Consumer<Integer> con=num -> System.out.println(num);
        con.accept(3);

        Consumer<String> sCon=s-> System.out.println(s.toUpperCase());
        sCon.accept("hello lambda");

        // list와 Consumer 같이 사용
        Consumer<String>consumer =s-> System.out.println(s.toUpperCase());
        List<String> list= Arrays.asList("abc","def","kgh");
        list.forEach(consumer);

        // 3. 입력을 받아 출력할 수 있는 인터페이스
        Function<Integer, String>fun=num->"input: "+num;
        String result=fun.apply(10);
        System.out.println(result);

        // 4. 참/거짓을 판정할 수 있는 인터페이스
        Predicate<Integer> pre=num->num>10;
        boolean res=pre.test(100);
        System.out.println(res);

        // 5. 단항 연산을 할 수 있는 인터페이스
        UnaryOperator<Integer> uOp=num->num*num;
        int k=uOp.apply(100);
        System.out.println(k);

    }
}

컬렉션 스트림에서 람다 사용

  • 람다와 컬렉션을 사용하지 않고 출력하는 방법
package book.chapter03.java8FunctionInterface;

public class Test2 {
    public static void main(String[] args) {

        Integer []ages={20,25,18,27,30,21,17,19,34,28};

        for (int age :ages) {
            if (age<20) {
                System.out.format("Age %d Can't enter\n",age);
            }
        }
    }

}
  • 컬렉션을 스트림을 이용한 방법의 장점
    - 선언적 프로그래밍
    • 의사소통 내용 자체가 그대로 코드로 구현되는 것
    • SQL 처럼 where 절과 같은 역할을 수행 가능
package book.chapter03.java8FunctionInterface;

import java.util.Arrays;

public class Test3 {

    public static void main(String[] args) {

        Integer []ages={20,25,18,27,30,21,17,19,34,28};
        
        Arrays.stream(ages)
                // stream에 filter안에는 predicate 함수형 인터페이스가 들어가 있다. 
                .filter(age->age<20)
                // forEach에는 consumer를 요구
                .forEach(age->System.out.format("Age %d Can't enter\n",age));
        
    }
}
  • Stream은 Collections Stream을 다른 컬렉션 스트림으로 변경 가능
  1. mapToInt / mapToDouble / mapToLong
    • Stream에서 Integer를 int로 변경
    • Integer를 double로 변경
    • Integer를 Long으로 변경
  2. sum, count, average, min, max 뿐만 아니라 집계 함수도 사용 가능
package book.chapter03.java8FunctionInterface;

import java.util.Arrays;

public class Test4 {
    public static void main(String[] args) {
        Integer []ages={20,25,18,27,30,21,17,19,34,28};

        // mapToInt -> Integer -> int로 변경
        int []test=Arrays.stream(ages).mapToInt(age->age).toArray();
        double []dTest=Arrays.stream(ages).mapToDouble(age->age).toArray();
        // 출력
        Arrays.stream(test).forEach(System.out::println);

        // 개수세기
        System.out.println(Arrays.stream(ages).count());
        // sum, average, min, max
        System.out.println(Arrays.stream(ages).mapToInt(age->age).sum());
        System.out.println(Arrays.stream(ages).mapToInt(age->age).average());
        System.out.println(Arrays.stream(ages).mapToInt(age->age).min());
        System.out.println(Arrays.stream(ages).mapToInt(age->age).max());

        System.out.println(Arrays.stream(ages).allMatch(age->age>20)); // 모두 조건을 만족하면 true
        System.out.println(Arrays.stream(ages).anyMatch(age->age>20)); // 하나라도 만족하면 true

        System.out.println(Arrays.stream(ages).findFirst());
        System.out.println(Arrays.stream(ages).findAny());

        // 정렬된 상태로 출력 
        Arrays.stream(ages).sorted().forEach(System.out::println);

    }
}

메소드 레퍼런스와 생성자 레퍼런스

메소드 레퍼런스란?!

  • 간략한 형식으로 변경하여 사용하는것
  • 인스턴스::인스턴스메소드
  • 클래스::정적메소드
  • 클래스::인스턴스멘소드
package book.chapter03.java8FunctionInterface;

import java.util.Arrays;
import java.util.function.BiFunction;

public class Test5 {

    public static void main(String[] args) {
        Double[]nums={1.0,4.0,9.0,16.0,25.0};

        Arrays.stream(nums)
                .map(num->Math.sqrt(num))
                .forEach(sqrtNum-> System.out.println(sqrtNum));

        // Method Reference
        Arrays.stream(nums)
                .map(Math::sqrt) // 클래스 :: 정적메소드
                .forEach(System.out::println); // 인스턴스 :: 인스턴스메소드

        BiFunction<Integer, Integer, Integer>bip_lambda=(a,b)->a.compareTo(b);
        BiFunction<Integer,Integer,Integer>bip_method_reference=Integer::compareTo; // 클래스:인스턴스메소드
        
        
        
    }
}

생성자 레퍼런스

  • 클래스::new
package book.chapter03.java8FunctionInterface;

import java.util.function.Supplier;

public class Test6 {

    public static void main(String[] args) {

        Test6 test=new Test6();

        Supplier<Test6> tt=Test6::new;

        Test6 test1=tt.get();
        Test6 test2=tt.get();

        System.out.println(test.hashCode());
        System.out.println(test1.hashCode());
        System.out.println(test2.hashCode());

    }
}

profile
BackEnd Developer

0개의 댓글