본문 바로가기
JAVA/객체지향

람다식

by chogigang 2023. 2. 25.

메서드를 하나의 식으로 표현한 것이며 람다식은 함수를 간략하면서도 명확한 식으로 표현할 수 있게 해줍니다.

메서드를 람다식으로 표현하면 메서드의 이름과 반환값이 없어지므로, 람다식을 익명함수 이라고도 합니다.

 

int[] arr = new int[5];
    Arrays.setAll(arr,(i)->(int)(Math.random()*5+1);

 

근본적으로 동일,함수는 일반적인 용어,   메서드는 객체지향개념용어 입니다

함수 클래스(독립적),         메서드는 클래스 (종속적) 입니다.

 

int max(int a,int b){
        return a>b? a:b;
}


(int a , int b){
        return a>b ? a: b;//람다식
    }

 

반환 값이 있는 메서드의 경우,return문 대신 식(expression)으로 대신 할 수 있습니다.

 

(int a,int b)   ->{return a>b ? a:b;}
    
    
    
    a>b ?a :b

 

 매개변수의 타입은 추론이 가능한 경우 생략할 수 있습고 대부분 생략 가능합니다

람다식에 반호나타입이 없는 이유도 항상 추론이 가능하기 때문 입니다.

괄호{}안에 문장이 하나일경우 괄호{}를 생략할 수 있습니다.

 

(String name, int i) ->{
        System.out.println(name+"="+i);
    }

    
    (String name, int i) ->
        System.out.println(name+"="+i);
    

 

 

그러나  괄호{}안의 문장이 return문일 경우 괄호{}를 생략할 수 없습니다.

 
(int a, int b) -> {return a>b ? a:b;}


 (int a, int b) -> return a>b ? a:b; //에러

 

 

    //기존
    int max(int a, int b) {
        return a > b ? a : b;
    }

//람다식
(int a, int b) -> {
        return a > b ? a : b;
    }

//return문 대신 expression 사용
(int a, int b) -> a > b ? a: b

//매개변수 타입 생략
    (a, b) -> a > b ? a : b

//매개변수 1개일 경우 괄호 생략
    a -> a*a     //OK
    int a -> a*a //에러

//본문 문장 1개일 경우 중괄호 생략
    (String name, int i) -> System.out.println(name+"="+i)

 

 메서드 람다식 
int max(int a, int b) {
return a > b ? a : b;
}
 (int a, int b) -> a > b ? a : b
int printVar(String name, int i) {
System.out.println(name+"="+i);
}
 (String name, int i) -> 
  System.out.println(name+"="+i)
int square(int x) {
return x*x;
}
 (int x) -> x*x
int roll() {
return (int)(Math.random() * 6);
}
 () -> (int)(Math.random() * 6)
int sumArr(int[] arr) {
int sum = 0;
for(int i : arr) 
sum += i;
return sum;
}
 (int[] arr) -> 
    int sum = 0;
    for(int i : arr)
        sum += i;
    return sum;
 }
int[] emptyArr() {
return new int[] {};
}
 () -> new int[]{}

 

 

 

함수형 인터페이스

 

람다식은 메서드와 동등한 것이 아니라 익명클래스의 객체와 동등합니다.

 / 람다식
            (int a, int b) -> a > b ? a : b

// 익명클래스의 객체
  new Object()  {
        int max(int a, int b) {
            return a > b ? a : b ;
        }
    }

 

람다식으로 정의된 익명 객체의 메서드를 호출하려면 참조변수가 필요합니다.
이 때, 참조변수의 타입은 클래스 또는 인터페이스가 가능한데,
람다식과 동등한 메서드가 정의되어 있는 것이어야 합니다.

// 예를 들어 max() 메서드가 정의된 Myfunction 인터페이스 정의
interface MyFunction  {
    public abstract int max(int a, int b);

    // MyFunction 인터페이스를 구현한 익명클래스 객체 생성
    MyFunction f = new MyFunction() {
        public int max (int a, int b);
  return a > b ? a : b;
    }
}
int big = f.max(5, 3);  //익명 객체의 메서드 호출

// 위의 익명 객체를 람다식으로 대체
MyFunction f = (int a, int b) -> a > b ? a : b;
int big = f.max(5, 3);

MyFunction 인터페이스를 구현한 익명 객체를 람다식으로 대체 가능한 이유는 람다식도 실제로는 익명 객체이고, MyFunction 인터페이스를 구현한 익명 객체의 메서드 max()와 람다식의 매개변수의 타입과 개수, 반환값이 일치하기 때문입니다.

단, 함수형 인터페이스에는 오직 하나의 추상 메서드만 정의되어 있어야 합니다.

그래야 람다식과 인터페이스가 1:1로 연결되기 때문이다.

반면 static 메서드와 default 메서드의 개수에는 제약이 없습니다.

@FunctionalInterface를 붙이면 컴파일러가 함수형 인터페이스를 올바르게 정의하였는지 확인해줍니다.

/ 기존 인터페이스의 메서드 구현
    List<String> list = Arrays.asList("abc", "aaa", "bbb", "ccc");
  Collections.sort(list, new Comparator<String>() {
        public int compare(String s1, String s2)  {
            return s2.compareTo(s1);
        }
    });

    // 람다식으로 구현
    List<String> list = Arrays.asList("abc", "aaa", "bbb", "ccc");
  Collections.sort(list, (s1, s2) -> s2.compareTo(s1));

 

 함수형 인터페이스로 람다식을 참조할 수 있지만 람다식의 타입이 함수형 인터페이스의 타입과 일차하는것은 아닙니다 람다는 익명 객체이고 익명 객체는 컴파일러가 임의로 이름을 정하기 때문에 알수가 없습니다

그래서 형변환이 필요합니다.

MyFunction f = (MyFunction) (()->{});

람다식은 MyFunction인터페이스 를 직접 구현하지 않았지만, 이 인터페이스를 구현한 클래스의 객체와

완전히 동일하기 때문에 위 같은 형변환을 허용합니다.

람다식은 이름없는 객체이지만 Object 타입으로 형변환 할 수 없습니다 오직 함수형 인터페이스로만 형변환이

가능합니다  Object 타입으로 형변환을 하려면 먼저 함수형 인텊이스로 변환후 형변환을 해야합니다.

 

Object obj = (Object)(MyFunction)(()->{});
String str = (Object)(MyFunction)(()->{})).toString();

 

 

java.util.function 패키지

 

일반적으로 자주 쓰이는 형식의 메서드는 미리 정의가 되어있습니다 재사용 유지보수 측면에서 인터페이스를 사용권장합니다

함수형 인터페이스 메서드 설명
java.lang.Runnable void run( ) 매개변수 x, 반환값 x
Supplier<T> T get( ) 매개변수 x, 반환값만 있음
Consumer<T> void accept(T t) 매개변수만 있고 반환값 없음
Function<T,R> R apply(T t) 하나의 매개변수를 받아 결과를 반환
Predicate<T> boolean test(T t) 조건식을 표현하는데 사용, 매개변수는 하나, 반환 타입은 boolean
BiConsumer<T,U> void accept(T t, U u) 두개의 매개변수만 있고 반환값이 없음.
BiPredicate<T,U> boolean test(T t, U u) 조건식을 표현하는데 사용, 매개변수는 둘, 반환 타입은 boolean
BiFunction<T,U,R> R apply(T t, U u) 두 개의 매개변수를 받아서 하나의 결과를 반환

 

메서드 참조

 람다식이 하나의 메서드만 호출하는 경우에는 메서드 참조로 람다식을 간략히 할 수 있습니다

하나의 메서드만 호출하는 람다식은 클래스이름::메서드이름 또는 참조변수::메서드이름으로 바꿀 수 있습니다

종류 람다식 메서드 참조
static 메서드 참조 (x) -> ClassName.method(x) ClassName::method
인스턴스 메서드 참조 (obj, x) -> obj.method(x) ClassName::method
특정 객체 인스턴스메서드 참조 (x) -> obj.method(x) obj::method

 

Supplier<MyClass> s = () -> new MyClass();//람다식
Supplier<MyClass> s = MyClass::new;//메서드 참조
Function<Integer, MyClass> f = (i) -> new MyClass(i);//람다식
Function<Integer, MyClass> f2 = MyClass::new;//메서드 참조
BiFunction<Integer, String, MyClass> bf = (i,s) -> new MyClass(i,s);//람다식
BiFunction<Integer, String, MyClass> bf2 = MyClass::new;//메서드 참조
Function<Integer, int[]> f = x -> new int[x];//람다식
Function<Integer, int[]> f2 = int[]::new;//메서드참조

 

 

 

Predicate 의 결합

여러 조건식 논리 연산자인 &&(and) , ||(or) , !(not)으로 연겨해서 하나의식을 구성할수있습니다.

Predicate를 and(), or(), negate()로 연결해서 하나의 새로운 Predicate로 결합할 수 있습니다

 

Predicate<Integer> p = i -> i < 100;
    Predicate<Integer> q = i -> i < 200;
    Predicate<Integer> r = i -> i%2 == 0;
    Predicate<Integer> notP = p.negate(); // i >= 100

    // 100 <= i && (i < 200 || i%2 == 0)
    Predicate<Integer> all = notP.and(q.or(r));
System.out.println(all.test(150));

 

 

메서드 참조

람다식을 더욱 간결하게 표현할수 있는 방법이 있습니다.

 

Function<String, Integer> f = (String s) -> Integer.parseInt(s);

Function<String, Integer> f = Integer::parseInt;

                   '클래스이름::메서드이름'또는'참조변수::메서드이름'으로 바꿀 수 있습니다.

 

 

생성자의 메서드 참조

 생성자를 호출하는 람다식도 메서드 참조로 변환할 수 있습니다.

 

upplier<MyClass> s = () -> new MyClass(); //람다식
Supplier<MyClass> s = MyClass::new; // 메서드 참조

Function<Integer, MyClass> s = (i) -> new MyClass(i); //람다식
Function<Integer, MyClass> s = MyClass::new // 메서드 참조

Function<Integer, int[]> f = (i) -> new int[i]; //람다식
Function<Integer, int[]> f = int[]::new; // 메서드 참조

메서드 참조는 람다식을 마치  static변수처럼 다룰 수 있게 해줍니다. 메서드 참조는 코드를 간략히 하는데

유용해서 많이 사용됩니다. 

'JAVA > 객체지향' 카테고리의 다른 글

스트림  (0) 2023.02.26
프로세스와 쓰레드  (0) 2023.02.24
제네릭,열거형  (0) 2023.02.24
래퍼 클래스,number클래스,문자열을 숫자로 변환  (0) 2023.02.23
String클래스  (0) 2023.02.23