Java

[Java] Lamda

웹개발자(진) 2024. 3. 27. 19:41
반응형
잡담

Java를 공부하면서 공부할 겸 코딩테스트 사이트를 많이 이용하는데 나는 아직 for문 밖에 못쓰는데 다른 사람들이 풀이 한거 보면 Thread도 많이 쓰고 lamda 표현도 사용해서 한줄만에 내가 몇줄 적어놓은 코드를 요약하더라고요... 현타가 많이오는데 현업자들 말로는 가독성도 중요하다고 하니까.. 열심히 해보겠습니다.


 

Lamda

Java에서 람다(lambda)는 Java 8부터 도입된 기능으로, 함수형 프로그래밍 스타일을 지원하기 위한 것입니다. 람다는 익명 함수(anonymous function)의 형태로 작성되며, 메서드를 하나의 식(expression)으로 표현할 수 있게 해줍니다.

기존의 Java에서는 익명 내부 클래스(anonymous inner class)를 사용하여 콜백이나 이벤트 핸들러 등을 구현했습니다. 그러나 이는 코드가 길고 가독성이 낮아지는 문제가 있었습니다. 람다를 사용하면 이러한 코드의 단순화와 가독성 향상을 기대할 수 있습니다.

람다 표현식은 다음과 같은 형태를 가집니다:

(parameters) -> expression

 

Thread에서의 람다사용


    package lamda

    class ThreadJob implements Runnable { // ThreadJob 클래스 선언 및 Runnable 인터페이스 구현
       
        @Override
        public void run() { // Runnable 인터페이스의 추상 메서드 run() 재정의
            System.out.println("HELLO~"); // "HELLO~" 출력
        }
    }

    public class ThreadExam_1 { // ThreadExam_1 클래스 선언

        public static void main(String[] args) { // main 메서드 시작
           
            new Thread(new ThreadJob()).start(); // 새로운 스레드 생성 후 시작
        }

    }


    package lamda;

    public class LamdaExam_2 {

        public static void main(String[] args) {
            new Thread( () ->{System.out.println("HELLO~");}).start();      //  ( () )는 매개변수가 없다는 뜻.
                                                                            // { }안에 내용이 run에서 실행하는 내용
        }                                                                   // 나머지 start()
    }  
                                                                       

이처럼 긴 코드를 한줄에 요약할 수 있다.

 


Lamda 함수형 인터페이스(Functional Interface)

Java에서 람다를 사용할 수 있는 함수형 인터페이스(Functional Interface)들이 다양하게 제공됩니다. 함수형 인터페이스는 하나의 추상 메서드만을 가지는 인터페이스입니다. 이러한 함수형 인터페이스들은 람다 표현식을 이용하여 간단하고 간결하게 코드를 작성할 수 있도록 도와줍니다.

Java 표준 라이브러리(java.util.function 패키지)에서는 다양한 함수형 인터페이스를 제공합니다. 그 중 일부를 아래에 설명합니다:

 

Consumer<T>

T 타입의 객체를 받아서 소비하는 함수형 인터페이스. void accept(T t) 메서드를 가집니다.


    package lamda;

    import java.util.function.BiConsumer;   //매개변수 2개받음(모든타입)
    import java.util.function.Consumer;     //매개변수 1개받음(모든타입)
    import java.util.function.IntConsumer;      //int 타입을 매개변수로 받는다.
    import java.util.function.ObjIntConsumer;   //object와 int 타입을 매개변수로 받는다.

    public class ConsumExam_3 {

        public static void main(String[] args) {
            Consumer<String> c = name -> System.out.println(name+" HELLO");    
            // 제네릭안에 넣어둔 타입으로 변수를받는다. 변수로 name이라는 문자열을 받음 (Consumer? => return값이 없다)
            c.accept("홍길동"); // 홍길동HELLO
           
            BiConsumer<String, Integer> bc = (name, age) -> System.out.println(name +" "+ age);
            // 변수로 name과 age를 받음(String, int)
            bc.accept("홍길자", 99);
           
            IntConsumer ic = age -> System.out.println("my age : " + age);  //IntConsumer는 Int 타입 변수만 받는다.
            ic.accept(20);
           
            ObjIntConsumer<String> oic = (obj, i) -> System.out.println(obj + "는 " + i);    
            //object 타입에 어떤 타입으로 받을지 제네릭안에 선언 int는 자동선언, 변수명 지정()
            oic.accept("커피", 1000);
        }

    }

Supplier<T>

T 타입의 객체를 공급하는 함수형 인터페이스입니다. T get() 메서드를 가집니다.


    package lamda;

    import java.util.function.Supplier;
    import java.util.function.BooleanSupplier;
    import java.util.function.IntSupplier;
    import java.util.function.DoubleSupplier;

    public class SupplierExam_4 {

        public static void main(String[] args) {
            Supplier<String> s = ()->"HELLO"; //HELLO라는 값을 return해준다.
            String result = s.get();            //get매서드를 사용하여 값을 가져올 수 있다.
            System.out.println(result);
           
            IntSupplier is = () -> 99;
            int iresult = is.getAsInt();    //getAsInt매서드를 사용하여 값을 가져올 수 있다.
            System.out.println(iresult);
        }

    }

Function<T, R>

T 타입의 객체를 받아서 R 타입의 객체를 리턴하는 함수형 인터페이스. R apply(T t) 메서드를 가집니다.


    package lamda;

    import java.util.function.Function;
    import java.util.function.BiFunction;   //입력 매개변수 2개 출력 매개변수 1개
    import java.util.function.IntFunction;
    import java.util.function.ToIntFunction;

    public class FunctionExam_5 {

        public static void main(String[] args) {
            Function<Integer, String> isf = num -> {return Integer.toString(num);};
            //int입력 String 출력 return 어떤식으로 할건지..
            String str = isf.apply(30);     //apply매서드
            System.out.println(str);
           
            IntFunction<String> iF = num -> {return Integer.toString(num);};
            String str2 = iF.apply(30);
            System.out.println(str2);

            BiFunction<Integer, String, String> bif = (num, name) -> num+name;  //입력 2개 출력 1개
            String str3 = bif.apply(100, "세");
            System.out.println(str3);
           
            ToIntFunction<String> tif = age -> {return Integer.parseInt(age);}; //변수를 받아서 int타입으로 출력한다.
            int i = tif.applyAsInt("130");
            System.out.println(i);
        }

    }



Predicate<T>

T 타입의 객체를 받아서 boolean 값을 리턴하는 함수형 인터페이스. boolean test(T t) 메서드를 가집니다.

UnaryOperator<T>

하나의 입력 값을 받아서 동일한 타입의 값을 리턴하는 함수형 인터페이스입니다. T apply(T t) 메서드를 가집니다.


    package lamda;

    import java.util.function.UnaryOperator;
    import java.util.function.IntBinaryOperator;
    import java.lang.reflect.Member;
    import java.util.function.BiFunction;
    import java.util.function.BiPredicate;
    import java.util.function.IntUnaryOperator;
    import java.util.function.Function;

    public class OperatorExam_6 {

        public static void main(String[] args) {
            UnaryOperator<String> uo = name -> {return "이름은 : " + name;};   //string을 입력받아서 string으로 출력하겠다.
            String str1 = uo.apply("홍길자");
            System.out.println(str1);
           
            IntBinaryOperator ibo = (num1, num2) -> {return num1 * num2;};
            int result = ibo.applyAsInt(3, 7);
            System.out.println(result);
           
            BiPredicate<String, String> bp = (s1, s2) -> {return s1.equals(s2);};
            boolean re1 = bp.test("홍길자", "홍길희");
            System.out.println("result : " +re1);
           
            IntUnaryOperator add = num -> num + 2;
            IntUnaryOperator mul = num -> num * 3;
           
            int result1 = add.compose(mul).applyAsInt(1);   //뒤에서 부터 계산(실행)
            System.out.println(result1);
            int result2 = add.andThen(mul).applyAsInt(1);   //앞에서 부터 계산(실행)
            System.out.println(result2);
           
            final int n = 10;
    //      n =11;
            IntUnaryOperator iu = x ->{
                System.out.println("x: " + x);
                System.out.println("n: " + n);
                return x + n;           //지역변수값을 사용하고싶으면 상수로 정의해줘야 한다.
            };
            int rint = iu.applyAsInt(9);
            System.out.println(rint);
           
            IntBinaryOperator intOp = Math::max;    //이미 만들어져있는 메서드를 들고와서 사용할수 도 있다.
            int r9 = intOp.applyAsInt(10, 20);
            System.out.println(r9);
           
            //Member m1 = new MeMember;
            BiFunction<String, Integer, Member> bb = Member::new;
            Member m1 = bb.apply("홍길수", 88);
            System.out.println(m1);
        }

    }

 


함수형 인터페이스를 활용

Member class

package lamda;

public class Member {
   
    private String name;
    private int height;
    private int weight;
    private String nation;
   
    public Member(String name, int height, int weight, String nation) {
        this.name = name;
        this.height = height;
        this.weight = weight;
        this.nation = nation;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public String getNation() {
        return nation;
    }

    public void setNation(String nation) {
        this.nation = nation;
    }

    @Override
    public String toString() {
        return "Member [name=" + name + ", height=" + height + ", weight=" + weight + ", nation=" + nation + "]";
    }
   
   
}

QFunction 함수형 인터페이스 선언

package lamda;

@FunctionalInterface
public interface QFunction<T, U, V, W, R> {
    R apply(T t, U u, V v, W w);
}

 QFunction 함수형 인터페이스를 Member 클래스 생성자에 참조

    package lamda;

    public class QFExam {

        public static void main(String[] args) {
           
            QFunction<String, Integer, Integer, String, Member> qf = Member::new;
            Member m = qf.apply("홍길춘",190,130,"연변");
            System.out.println(m.toString());
        }

    }

위의 코드는 함수형 인터페이스를 활용하여 새로운 객체를 생성하는 예시를 보여줍니다. 주요 내용은 다음과 같습니다:

  1. Member 클래스: Member 클래스는 이름(name), 키(height), 몸무게(weight), 국적(nation)을 나타내는 필드와 이를 초기화하는 생성자, getter 및 setter 메서드, 객체 정보를 문자열로 반환하는 toString() 메서드를 가지고 있습니다.
  2. QFunction 인터페이스: QFunction은 네 개의 입력 파라미터(T, U, V, W)를 받아 새로운 객체(R)를 생성하는 함수형 인터페이스입니다. apply 메서드를 통해 이 기능을 수행합니다.
  3. QFExam 클래스: main 메서드에서는 QFunction 인터페이스를 이용하여 Member 객체를 생성하는 예시를 보여줍니다. QFunction의 apply 메서드를 호출할 때, Member 클래스의 생성자를 참조하여 새로운 Member 객체를 생성합니다. 이를 통해 "홍길춘"이라는 이름, 190이라는 키, 130이라는 몸무게, "연변"이라는 국적을 갖는 Member 객체가 생성되고, 해당 객체 정보를 출력합니다.

따라서 위 코드는 함수형 인터페이스를 이용하여 객체를 생성하는 예시를 보여주고 있습니다.

반응형