티스토리 뷰

1.    람다식 ( Lamda Expression )

-      자바8부터 함수적 프로그래밍을 위해 람다식 지원하기 시작했다.

-      익명 함수(Anonymous function)를 생성하기 위한 식이다.

-      람다식을 통해서 코드가 매우 간결해지고, 컬렉션 요소(대용량 데이터)를 필터링 또는 매핑하기 쉽게 집계해준다.

-      자바는 람다식을 함수적 인터페이스의 익명 구현 객체로 취급한다.

-      람다식이 구현대상이 되는 함수적 인터페이스를 해당 람다식의 Target Type이라 부른다.

-      모든 인터페이스가 람다식의 구현 대상이 될 수는 없다.

-      람다식의 조건 : 구격(추상메소드)이 오로지 단 1 개만 있는 인터페이스

-      람다식의 조건에서는 추상메소드의 개수만 확인하기 때문에 상수나 디폴트 메소드의 개수는 2개 이상이어도 상관이 없다.

-      인터페이스의 위에 @FunctionalInterface를 붙이면 추상 메소드가 1개인지 확인해 주는 기능을 해주는데, 이를 사용한다.

 

2.    람다식 기본 문법

-      ( 타입 매개변수 ) - > { 실행문 ; }

-      Ex. ( int a ) - > { System.out.println(a) ; }

-      람다식은 다음과 같은 조건에 해당하면 생략이 가능한데, 최대한 람다식을 생략하는 것이 좋다.

-      1 ) 매개변수 타입(int)은 런타임시에 대입 값(a)에 따라 자동으로 인식이 가능하기에 생략이(int a -> a) 가능하다.

-      2 ) 하나의 매개변수만 있을 경우에는 ( )는 생략이 가능하다.

-      3 ) 하나의 실행문만 있다면 중괄호 { }는 생략이 가능하다.

-      4 ) 매개변수가 없을 경우에는 ( )는 생략이 불가능하다.

-      5 ) 리턴값이 있는 경우에는 return을 사용해야 한다.

-      6 ) 중괄호 { }return문만이 있을 경우에는, 중괄호를 생략하는 것이 가능하다.

 

3.    타겟타입

-      타겟 타입은 람다식이 대입되는 인터페이스로, 익명 구현 객체를 만들 때 사용하는 인터페이스를 의미한다.

-      Ex. 인터페이스 변수 = 람다식;

-      Ex. MyfunctionalInterface fi;

-      Ex. fi = () -> System.out.println("method call2");

-      Ex. fi.method();

 

4.    자바 표준 API

-      자바 8부터는 표준 API로 제공되는 함수적 인터페이스는 java.util.function 패키지에 포함되어 있다.

-      매개타입으로 사용되어 람다식을 매개 값으로 대입할 수 있도록 한다.

-      1 개의 추상 메소드를 가지고 있는 인터페이스들은 모두 람다식을 사용할 수 있다.

-      인터페이스에 선언된 추상 메소드의 매개 값과 리턴 값의 유무에 따라서 구분된다.

 

-      함수적 인터페이스의 종류 :

 

-      1 ) Consumer 함수적(functional) 인터페이스 :

-      매개 값만 있고 리턴 값이 없는 추상 메소드를 가진다.

-      매개 값을 가지고 데이터를 소개하는 함수적 인터페이스다.

-      추상 메소드 : void accept ( 매개변수 )

 

-      2 ) Supplier 함수적 인터페이스 :

-      매개 값은 없고 리턴 값만 있는 추상 메소드를 가진다.

-      추상 메소드 : 타입 + ( get+As+타입 ) ( ) / get ( )

 

-      3 ) Function 함수적 인터페이스 :

-      매개 값과 리턴 값이 모두 있는 추상 메소드를 가진다.

-      주로 매개 값을 리턴 값으로 타입변환(매핑)을 하는 경우 사용한다.

-      이때 타입 변환은 강제형변환이나 자동형변환을 의미하는 것은 아니다.

-      추상 메소드1 : 변화 후 타입 + ( apply+As+변화 후 타입 ) ( 변하기 전 타입 ) /

-      추상 메소드2 : 변화 후 타입 + apply ( 변하기 전의 타입 + 매개 변수  )

 

-      4 ) Operator 함수적 인터페이스 :

-      매개 값과 리턴 값이 모두 있는 추상 메소드를 가진다.

-      매개 값을 연산하고 그 결과를 리턴하는 경우에 사용한다.

 

-      5 ) Predicate 함수적 인터페이스 :

-      매개 값을 조사해서 true 또는 false를 리턴할 때 사용한다.


01. 람다식 - 1st case. 매개변수와 리턴값이 없는 경우

 

package no_argument_no_return;

 

 

@FunctionalInterface // 추상 메소드의 개수 확인

public interface MyfunctionalInterface { // 람다식 - @FunctionalInterface ( 함수적 인터페이스 )

      

       public abstract void method(); // 1st case

      

//     public abstract void method (int age); // 2nd case - 매개변수가 있는 버전

      

//     public abstract int method3(); // 3rd case - 리턴 타입이 있는 버전

      

//     public abstract int method4 ( int age ); // 4th case - 리턴타입과 매개변수가 있는 버전

      

       // 추상 메소드가 1개가 아니라 2개 이상이면 오류가 발생한다.

      

       // 각 케이스에 따라서 람다식의 작성 방법이 달라진다.

 

} // end interface


package no_argument_no_return;

 

public class MyfunctionalInterfaceExampel {

      

       public static void main(String[] args) { // 람다식 (***)

            

             MyfunctionalInterface fi; // functionalInterface타입의 변수 생성 = 람다식 적용 가능!

            

//           ======================================

            

             // 1. 람다식 적용 ( 1st case )

            

             fi = () -> {

                    // ( )는 public abstract void method();에 있는 ( )를 그대로 가져 온 것으로

                    // 가져온 이후에 { };를 통해 오버 라이딩을 하는 것이다.

                   

                    String str = "method call"; // 값을 넣어 주고

                    System.out.println(str); // 이를 출력하는 메소드로 오버 라이딩하였다.

                   

                    // 1st case의 경우 리턴타입이 없기에 리턴값을 주지 않는다.

                   

             }; // 람다식 : f1에 선언된 추상 매소드를 오버라이딩하는 익명구현객체를 생성 (**)

            

             fi.method(); // fi에 선언된 추상 메소드 호출 ( 다형성 2 )

            

//           ======================================

            

             // 2-1 인터페이스의 구현객체 작성방법 : 직접 구현 클래스 선언

            

             fi = new MyfunctionalInterfaceImpl(); // 다형성 1

             fi.method(); // 다형성 2

            

//           ======================================

            

             // 2-2 인터페이스의 구현객체 작성방법 : 익명구현객체 코딩기법

            

             fi = new MyfunctionalInterface() {

 

                    @Override

                    public void method() {

                          

                           System.out.println("Anonymous::method() invoked.");

                          

                    } // 메소드

                   

             }; // 익명구현객체 생성

            

             fi.method(); // 다형성 2

            

//           ======================================

            

             // 2-3 인터페이스의 구현객체 작성방법 : 람다식으로 구현하자!!

            

             fi = () -> { // 다형성 1 적용 - 오버라이드

                    System.out.println("Anonymous by Lambda::method() invoked.");

             };

            

             fi.method();

            

//           ======================================

            

             // 2-1과 2-2의 방법은 추상 메소드의 개수가 몇개인지 상관이 없지만

             // 2-3의 방법인 람다식은 메소드의 이름을 가져오지 않기에 추상 메소드가 1개일때만 사용이 가능하다!

            

             // , 람다식을 사용하면 코드가 매우 간결해지는 장점이 있다.

            

//           ======================================

            

             // 3. 오버라이딩된 실행문이 1개면 { };는 생략이 가능하다!

            

             fi = () -> System.out.println("method call2");

            

             fi.method();

            

//           ======================================

            

             fi = () -> {

                    System.out.println("method call3");

             };

            

             fi.method();

            

//           ======================================

            

             // 출력 :

            

//           method call

//           MyfunctionalInterfaceImpl::method() invoked.

//           Anonymous::method() invoked.

//           Anonymous by Lambda::method() invoked.

//           method call2

//           method call3

            

//           ======================================

            

       } // main

 

} // end class



02. 람다식 - 2nd case. 매개변수가 있는 경우

 

package no_argument_no_return;

 

 

@FunctionalInterface // 추상 메소드의 개수 확인

public interface MyfunctionalInterface2 { // 람다식 - @FunctionalInterface ( 함수적 인터페이스 )

      

//     public abstract void method(); // 1st case

      

       public abstract void method (int age); // 2nd case - 매개변수가 있는 버전

      

//     public abstract int method3(); // 3rd case - 리턴 타입이 있는 버전

      

//     public abstract int method4 ( int age ); // 4th case - 리턴타입과 매개변수가 있는 버전

      

       // 추상 메소드가 1개가 아니라 2개 이상이면 오류가 발생한다.

      

       // 각 케이스에 따라서 람다식의 작성 방법이 달라진다.

 

} // end interface


package no_argument_no_return;

 

public class MyfunctionalInterfaceExampel2 {

      

       public static void main(String[] args) { // 람다식 - 매개변수 있는 타입 (***)

            

             MyfunctionalInterface2 fi; // functionalInterface타입의 변수 생성 = 람다식 적용 가능!

            

//           ======================================

            

             // 1. 람다식 적용 ( 2nd case )

            

             fi = (int age) -> {

                    System.out.println("Anonymous::method2 invoked.-1");

             };

            

             fi.method(23);

            

//           ======================================

            

             // 2. 생략 1 - > 하나의 실행문이면 생략가능 & 매개변수의 타입은 생략이 가능하다.

            

             fi = (age) -> System.out.println("Anonymous::method2 invoked.-2");

             fi.method(12);

            

//           =====================================

            

             // 3. 생략 2 - 하나의 매개변수이면 생략 가능!

            

             fi = age -> System.out.println("Anonymous::method2 invoked.-3");

             fi.method(25);

            

//           =====================================

            

             // 출력 :

            

//           Anonymous::method2 invoked.-1

//           Anonymous::method2 invoked.-2

//           Anonymous::method2 invoked.-3

                          

       } // main

 

} // end class



03. 람다식 - 3rd case. 리턴값이 있는 경우

 

package no_argument_no_return;

 

 

@FunctionalInterface // 추상 메소드의 개수 확인

public interface MyfunctionalInterface3 { // 람다식 - @FunctionalInterface ( 함수적 인터페이스 )

      

//     public abstract void method(); // 1st case

      

//     public abstract void method (int age); // 2nd case - 매개변수가 있는 버전

      

       public abstract int method3(); // 3rd case - 리턴 타입이 있는 버전

      

//     public abstract int method4 ( int age ); // 4th case - 리턴타입과 매개변수가 있는 버전

      

       // 추상 메소드가 1개가 아니라 2개 이상이면 오류가 발생한다.

      

       // 각 케이스에 따라서 람다식의 작성 방법이 달라진다.

 

} // end interface


package no_argument_no_return;

 

public class MyfunctionalInterfaceExampel3 {

      

       public static void main(String[] args) { // 람다식 - 리턴타입이 있는 타입 (***)

            

             MyfunctionalInterface3 fi; // functionalInterface타입의 변수 생성 = 람다식 적용 가능!

            

//           ======================================

            

             // 1. 람다식 적용 ( 3st case )

            

             fi = () -> {

                    int number1 = 100;

                    int number2 = 200;

                   

                    return number1 + number2;

             };

            

             fi.method3();

            

//           ======================================

            

             // 2. 생략 1 - 리턴값의 생략

            

             fi = () -> 100+300; // 타입에 맞게 값을 생성하는 것이면 리턴 값이 있을 확률이 높다.

            

             fi.method3();

            

//           ======================================

            

             // 3. 생략 2 - 리턴값의 생략

            

             int value = 100;

            

             fi = () -> value; // 타입에 맞게 값만 생성할 수 있으면 메소드도 된다.

            

             fi.method3();

            

//           ======================================

            

             // 4. 생략 3

            

             int value1 = 200;

            

             fi = () -> 220 + value1;

            

             fi.method3();

                          

       } // main

 

} // end class



04. 람다식 - 4th case. 매개변수와 리턴값이 모두 있는 경우

 

package no_argument_no_return;

 

 

@FunctionalInterface // 추상 메소드의 개수 확인

public interface MyfunctionalInterface4 { // 람다식 - @FunctionalInterface ( 함수적 인터페이스 )

      

//     public abstract void method(); // 1st case

      

//     public abstract void method (int age); // 2nd case - 매개변수가 있는 버전

      

//     public abstract int method3(); // 3rd case - 리턴 타입이 있는 버전

      

       public abstract int method4 ( int age ); // 4th case - 리턴타입과 매개변수가 있는 버전

      

       // 추상 메소드가 1개가 아니라 2개 이상이면 오류가 발생한다.

      

       // 각 케이스에 따라서 람다식의 작성 방법이 달라진다.

 

} // end interface


package no_argument_no_return;

 

public class MyfunctionalInterfaceExampel4 {

      

       public static void main(String[] args) { // 람다식 - 리턴타입과 매개변수가 있는 타입 (***)

            

             MyfunctionalInterface4 fi; // functionalInterface타입의 변수 생성 = 람다식 적용 가능!

            

//           ======================================

            

             // 1. 람다식 적용 ( 4st case )

            

             fi = (age) -> {

                    return 1+2*3-4;

             };

            

             fi.method4(23);

            

//           ======================================

            

             // 2. 생략1 - 강제형변환

            

             fi = age -> (int) ( 2+5+1*7+9-5/3.14);

            

             fi.method4(23);

            

//           ======================================

            

             // 3. 생략2 - 매개변수 간단하게 표시

            

             fi = a -> (int)(1+2*3 - 4 / a);

            

             System.out.println("fi : " + fi);

             // 출력 : fi : no_argument_no_return.MyfunctionalInterfaceExampel4$$Lambda$3/0x0000000800c01218@85ede7b

            

             // 이 중 $$Lambda$3/0x0000000800c01218@85ede7b가 람다식을 의미한다.

             // $3는 이 클래스 내에서 이 람다식이 몇 번째 람다식인지 의미하는 것이다.

            

//           ======================================

            

//           fi = a -> {  // this를 출력하고 싶어도 static 메소드 안에서는 정적 멤버만 사용가능해서 출력이 불가능하다.

//                  System.out.println(this);

//                  return 1000;

//           };

            

             // 그렇다면 인스턴스 안에서는 ...? - - > TTT 클래스에서 생성

            

//           ======================================

            

             TTT ttt = new TTT (); // 인스턴스이기에 객체 생성

            

             System.out.println("1. ttt : " + ttt);

             // 출력 : 1. ttt : no_argument_no_return.TTT@63961c42

            

             ttt.instanceMethod(23);

             // 출력 : no_argument_no_return.TTT@6b884d57

            

             // 출력 결과가 같은 것을 알 수 있다.

            

             // 람다식의 내부에서 this는 람다식이 만든 익명구현객체의 주고를 가지고 있지 않고 (***)

             // 오히려 람다식을 포함하고 있는 바깥타입의 객체의 주소를 가지고 있다!!

             // 그렇기에 ttt클래스에 있는 필드를 람다식 블록 내에서 사용하는 것이 가능하다.

            

             // + 익명구현객체는 힙영역에 생성된다.

            

             // 출력 :

            

//           1. ttt : no_argument_no_return.TTT@63961c42

//           2. no_argument_no_return.TTT@63961c42

//           3. name : Yoseph

//           4. age : 23

            

//           ======================================

            

             fi = new MyfunctionalInterface4() {

                   

                    @Override

                    public int method4(int age) {

                           System.out.println(this);

                           return 1000;

                    }

             };

            

             fi.method4(23); // 출력 : no_argument_no_return.MyfunctionalInterfaceExampel4$1@63961c42

                          

       } // main

 

} // end class



05. 람다식 - 클로져

 

package no_argument_no_return;

 

import lombok.NoArgsConstructor;

 

@NoArgsConstructor

public class TTT {

      

       String name = "Yoseph";

       int age = 23;

      

       private void instanceMethod2() {;;} // instanceMethod2

      

       public void instanceMethod(int param) { // 4th case - 리턴타입과 매개변수가 있는 버전

            

//           final int temp = 333;

             int temp = 333;

//           param = 23333; // 람다 내부에서 사용하는 지역변수는 자동으로 final이 붙여지기에 값을 바꿀 수가 없다. ( 신 버전 )

      

             MyfunctionalInterface4 fi = a -> {

                    System.out.println("2. " + this); // 여기서 this는 람다식을 가르치지 않고 ttt를 의미하기에 name과 age를 사용할 수 있다!! (**)

                    System.out.println("3. name : " + this.name);

                    System.out.println("4. age : " + this.age);

                   

                    this.instanceMethod2(); // 메소드도 사용이 가능하다!

                   

                    System.out.println("5. param : " + param);

                    System.out.println("6. temp : " + temp);

                   

                    return 777;

             }; // 람다식

            

             // name이나 age는 블록이 끝나게 되면 파괴되지만,

             // 람다식을 통해서 만들어지는 익명구현객체는 힙영역에서 파괴되지 않고 살아있는데

             // 그렇게 되면 System.out.println("3. name : " + this.name);와 같이 필드를 가지고 왔을 경우

             // 파괴된 시점이 달라서 예외가 발생될 수 있다. - - - > 이러한 문제를 "클로져"라고 한다.

            

//           정리 :

             // 생명주기가 다른 지역변수를 익명객체가 사용한 경우,

             // 먼저 파괴되는 지역변수의 값을 익명객체가 결정할 수 없는 상태에 빠지는데

             // 이 상태를 "Closure(클로져)"라고 한다!!

            

             // 이때 final을 붙여 상수로 만들게 되면, clazz객체가 저장되는 메소드영역에 저장되기 때문에 생명이 길어지게 된다.

             // 이를 통해서 클로져 문제를 해결하였다. ( 구 버전 )

             // ? 그런데 그러면 변수의 값을 바꿀 수가 없잖아!!

            

             // 람다 내부에서 사용한 변수는 컴파일러가 실행할 때 자동으로 final을 붙여준다. ( 신 버전 )

             // 그렇다 해도, 그냥 람다 내부에서는 지역변수 사용하는 것을 최대한 지양하는 것이 좋다.

            

             // 또한 람다식은 클래스 내부에서 바로 사용되는 것이 아니라, 메소드 블록 내에서만 사용이 가능하다!!

            

             fi.method4(23); // 오버 라이딩하도록 작성해야 한다!!(**)

            

       } // instanceMethod

 

} // end class



06. 람다식 연습

 

package sample;

 

 

public class Sample1 {

      

       public static void main(String [] args) { // 람다식 연습 1

             Runnable task = null;

            

//           Runnable은 java.lang에 있는 @FunctionalInterface이기에 활용이 가능하다.

//           public abstract void run();

            

             task = () -> System.out.println("실행이 되고 있습니다.");

             task.run();

            

//           ===============================================================

            

             I1 i1;

            

//           public abstract void method1 ( String name, int age );

            

             i1 = (n, a) -> System.out.println("이름은 " + n + "이며, 나이는 " + a + "입니다."); // 변수명을 간결하게 바꿀수도 있다.

             i1.method1("Yoseph", 23);

            

//           ===============================================================

            

             I2 i2;

            

//           public abstract double method2 ( String name, int age ); // 자동형변환을 생각해야 한다.

            

             i2 = (n, a) -> {

                   

                    System.out.println("이름은 " + n + "이며, 나이는 " + a + "입니다.");

                   

                    return (double)a; };

                   

                    i2.method2("Yoseph", 23);

            

       } // main

 

} // end class



07. Consumer 함수적 인터페이스

 

package consumer;

 

import java.util.function.BiConsumer;

import java.util.function.Consumer;

import java.util.function.DoubleConsumer;

import java.util.function.ObjIntConsumer;

 

 

// 데이터를 변수로 받고 돌려주는 것은 없는 데이터 소비로직을 사용 ( 규격화 )

public class ConsumerExample {

      

       public static void main(String [] args) {

            

//           void accept(T t);

            

             Consumer<String> consumer = t -> System.out.println(t + "8"); // import문 사용!!! - 다형성 1

            

             consumer.accept("java"); // 다형성 2

            

//           ===================================================================

            

//           void accept(T t, U u);

            

             BiConsumer<String, String> bigConsumer = (t,u) -> System.out.println(t+u);

             bigConsumer.accept("java", "8");

            

//           ===================================================================

            

//           void accept(double value);

            

             DoubleConsumer doubleConsumer = d -> System.out.println("java" + d);

             doubleConsumer.accept(8.0);

            

//           ===================================================================

            

//           void accept(T t, int value);

            

             ObjIntConsumer<String> objIntConsumer = (t,i) -> System.out.println(t+i); // 람다식에서는 메소드명을 사용하지 않는다.

             objIntConsumer.accept("java", 8);

            

       }// main

 

} // end class



08.  Supplier 함수적 인터페이스

 

package supplier;

 

import java.util.function.IntSupplier;

 

public class SupplierExample {

      

       public static void main (String [] args) {

            

//           int getAsInt();

            

             IntSupplier intsupplier = () -> {

                   

                    int num = (int) ( Math.random() * 6) +1;

                   

                    return num;

                   

             }; // 람다식

            

             int num = intsupplier.getAsInt();

             System.out.println("눈의 수 : "+ num);

            

       } // main

 

} // end class

 



09.  Function 함수적 인터페이스

 

package function;

 

import java.util.Arrays;

import java.util.List;

import java.util.function.Function;

import java.util.function.ToIntFunction;

 

public class FunctionExample1 { // function (****)

      

       // 1. 여러 요소를 목록으로 보관하는 타입 List

       public static List<Student> list = Arrays.asList( // Import!!

                   

                    new Student("홍길동",90,63),

                    new Student("신용권",95,93)

                   

                    );

      

//      ===================================================================================

      

      

       // 2. Function 인터페이스의 목적 : 매핑 ( A -> B )

       public static void printString(Function<Student, String> function) {

            

//           R apply(T t);

            

             for (Student student : list ) {

                    System.out.print(function.apply(student)+ " ");

             } // enhanced for

            

             System.out.println();

            

       } // printString

      

//      ===================================================================================

      

       public static void printInt(ToIntFunction<Student> function) {

            

//           int applyAsInt(T value);

            

             for (Student student : list ) {

                    System.out.print(function.applyAsInt(student)+ " ");

             } // enhanced for

            

             System.out.println();

            

       } // printInt

      

//      ===================================================================================

      

       public static void main (String [] args) {

            

             System.out.println("[ 학생 이름 ] ");

            

             FunctionExample1.printString(

                          

                           t -> t.getName() // 람다식으로 익명구현객체를 생성하여 전달 ( 전달 인자 )

                          

                           ); // 람다식

            

//           ===================================================================================

            

             System.out.println(" [ 영어 점수 ] "); // Getter 메소드 활용

             FunctionExample1.printInt(t -> t.getEnglishScore());

            

//           ===================================================================================

            

//           int applyAsInt(T value); // 람다식 - 익명구현객체 활용

            

             ToIntFunction<Student> mapping = v -> v.getEnglishScore();

            

             for( Student student : list ) {

                   

                    int mathScore = mapping.applyAsInt(student);

                   

                    System.out.println(mathScore);

                   

//                  System.out.println(mapping.applyAsInt(student)); // 이렇게 작성할 수도 있다. 이 방법을 권장한다.

                   

             } // enhanced for

            

//           ===================================================================================

            

             System.out.println(" [ 수학 점수 ] "); // Setter 활용

             FunctionExample1.printInt(t -> t.getMathScore());

            

       } // main

 

} // end class

728x90
댓글
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
최근에 올라온 글
Total
Today
Yesterday
공지사항