(람) -> {다}

TIL·2023년 1월 17일
0

Java (최은빈)

목록 보기
24/27

  • JDK8+
  • 인터페이스의 인스턴스 생성 후(new 생략) 추상메서드를 익명 객체 대신 람다로 구현
    • (매개변수) -> {실행코드}
    • 구현할 함수를 인터페이스(@FunctionalInterface)가 제공 (함수지향)
  • from 람다 미적분학
    • funtinc chaining too
    • 함수가 완전히 적용될 때까지 재귀적 발생하고 최종 결과 반환



// 익명 객체
Comparator<Student> comparator = new Comparator<>() {
		public int compare(Student o1, Student o2) { Integer.compare(o1.score, o2.score) }
}

// 람다
Comparator<Student> comparator = (o1, o2) -> { Integer.compare(o1.score, o2.score) }



  • compare() 재정의
  • Comparator.java is @FunctionalInerface
  • int compare(T o1, T o2); of Comparator.java is only one abstract method
  • syntatic sugar
    • Comparator의 추상메서드는 오직 하나 이므로 함수의 정의(이름, 데이터 타입) 생략 가능 (나머지는 default, static, extend)
    • 런타임시 내부적으로 익명 구현 객체를 생성함



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

        // 인터페이스는 인스턴스 만들 수 없지만 추상 메서드 구현하면 가능
        CustomFunctionInterface1 f0 = new CustomFunctionInterface1() {
            @Override
            public void lambda() {
                System.out.println("hello world");
            }
        };

        // => 람다로 한줄 가능
        // 구현부 한줄 이면 중괄호 생략 가능. 여러줄 이면 생략 불가
        CustomFunctionInterface1 f1 = () -> System.out.println("hello world");
        f1.lambda();

        // 파라미터, 반환값 없으면 뭐든지 다 구현 가능함을 의미
        CustomFunctionInterface1 f12 = () -> System.out.println("hello lambda");
        f12.lambda();

        // 인스턴스 아닌 클래스로 호출
        CustomFunctionInterface1.staticMethod();
        // default, static은 인터페이스에서 여러개 정의 가능
        f1.defaultMethod();
        f12.defaultMethod();
}



@FunctionalInterface // 추상 메서드 2개되면 컴파일 에러 잡아줌 (@Retention(RetentionPolicy.RUNTIME)
public interface CustomFunctionInterface1 {

    // abstract method
    void lambda(); // 매개변수, 반환값 없음
//    void lambda2();

    // default method
    // 인스턴스(외부)에 제공
    default void defaultMethod() {
        System.out.println("defaultMethod");
    }

    // static method
    static void staticMethod() {
        System.out.println("staticMethod");
    }

    // public
    // 인터페이스는 기본 접근 제어자가 public (외부 객체에 메서드 제공하는 역할 이므로)
}



문법

void lambda();
void lambda(T arg);
void lambda(T... args);
int lambda(int i1, int i2);
int lambda(int i1, int i2);

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

        // 0. 인터페이스는 인스턴스 만들 수 없지만 추상 메서드 구현 하면 가능 (익명 객체)
        CustomFunctionInterface1 f0 = new CustomFunctionInterface1() {
            @Override
            public void lambda() {
                System.out.println("hello world");
            }
        };

        // => 람다로 한줄 가능
        // 구현부 한줄 이면 중괄호 생략 가능. 여러줄 이면 생략 불가
        CustomFunctionInterface1 f1 = () -> System.out.println("hello world");
        f1.lambda();

        // 1. void, ()
        CustomFunctionInterface1 f12 = () -> System.out.println("hello lambda");
        f12.lambda();

        // 인스턴스 아닌 클래스로 호출
        CustomFunctionInterface1.staticMethod();
        // default, static은 인터페이스에서 여러개 정의 가능
        f1.defaultMethod();
        f12.defaultMethod();


        // 2. void, T
        CustomFunctionInterface2.staticMethod(); // 인스턴스 생성 전 호출 가능
        CustomFunctionInterface2<Object> f2 = a -> System.out.println(a);
        // <Object> 이므로 모든 데이터 타입 인자로 가능 (default 이므로 생략 가능)
        f2.lambda(10);
        f2.lambda("hello world");
        f2.lambda(new Something("hello world", 10));
        f2.defaultMethod();
        System.out.println();


        // 3. void, T...(가변 인자)
        CustomFunctionInterface3.staticMethod();
        CustomFunctionInterface3 f3 = arguments -> {
            for (int i = 0; i < arguments.length; i++) {
                System.out.print(arguments[i] + ",");
            }
            System.out.println();
//            return arguments;
        };

        f3.lambda();
        f3.lambda(10);
        f3.lambda(10, 20);
        f3.lambda(10, 20, 30);
        f3.lambda(10, 20, 30, 40, 50);
        f3.defaultMethod();
        System.out.println();


        // 4. int, 인자 2개
        CustomFunctionInterface4.staticMethod();
        CustomFunctionInterface4 f4 = (a, b) -> a + b;
        System.out.println(f4.lambda(1, 2));
        System.out.println(f4.lambda(3, 4));
        System.out.println(f4.lambda(100, 200));
        System.out.println(f4.lambda(300, 400));

        f4.defaultMethod();


        // 5. T, T...
        // Object default => 덧셈 아닌 결합
        CustomFunctionInterface5.staticMethod();
        CustomFunctionInterface5 f5 = arguments -> {
            String str = "";
            for (Object arg: arguments) {
                str += arg;
            }
            return str;
        };
        System.out.println(f5.lambda());
        System.out.println(f5.lambda(1));
        System.out.println(f5.lambda(1, 2));
        System.out.println(f5.lambda(1, 2, 3, 4));
        System.out.println(f5.lambda("a"));
        System.out.println(f5.lambda("a", "b"));
        System.out.println(f5.lambda("a", "b", "c"));
        System.out.println(f5.lambda("a", "b", "c", "d"));
        f5.defaultMethod();

    }
}



java.util.function

  • 인터페이스 직접 만들 필요 없이 자바에서 제공 (@FunctionalInterface)
  • 인터페이스의 인스턴스 생성(new X), 추상 메서드 호출, 추상 메서드 람다로 구현



java.util.function.Consumer

  • 매개O, 리턴X (소비)
public class ConsumerExample {
    public static void main(String[] args) {
        // 1. 인스턴스 생성(new 생략) + 추상 메서드 람다 구현 / 2. 추상 메서드 호출
        Consumer<String> consumer = t -> System.out.println(t + "8");
        consumer.accept("java");

        BiConsumer<String, String> biConsumer = (t, u) -> System.out.println(t + u);
        biConsumer.accept("java", "8");

        DoubleConsumer doubleConsumer = d -> System.out.println("Java" + d);
        doubleConsumer.accept(8.0);

        ObjIntConsumer<String> objIntConsumer = (t, i) -> System.out.println(t + i);
        objIntConsumer.accept("Java", 8);
    }
}



java.util.function.Supplier

  • 매개X, 리턴O (제공)
public class SupplierExample {
    public static void main(String[] args) {
        // 1. 인스턴스 생성(new 생략) + 추상 메서드 람다 구현 / 2. 추상 메서드 호출
        Supplier<String> supplier = () -> "java8";
        System.out.println(supplier.get());

        IntSupplier intSupplier = () -> (int) (Math.random() * 45) + 1;
        System.out.println(intSupplier.getAsInt());
    }
}



java.util.function.Function

  • 매개O, 리턴O
  • 타입 매핑 시킬때 주로 사용
    • int -> Integer, String, ..
public class FunctionExample {
    private static List<Student> students = Arrays.asList(
            new Student("kim", 20, 80),
            new Student("lee", 25, 50),
            new Student("park", 29, 100)
    );

    // 1. 인터페이스의 인스턴스 생성 : Function<Student, String> function
    // public interface Function<T, R> {
    public static String mapToString(Student student, Function<Student, String> function) {
        // 2. 구현한 추상 메서드를 인스턴스로 호출
        return function.apply(student);
    }

    public static int mapToInt(Student student, ToIntFunction<Student> function) {
        return function.applyAsInt(student);
    }

    public static double average(ToIntFunction<Student> function) {
        int sum = 0;
        for (Student student: students) {
            sum += function.applyAsInt(student);
        }
        return (double)sum / students.size();
    }


    // 3. 추상 메서드 람다로 구현 : mapToString(student, t -> t.getName()
    // R apply(T t);
    public static void main(String[] args) {
        for (Student student: students) {
            System.out.println(mapToString(student, t -> t.getName()));
        }
        System.out.println();

        for (Student student: students) {
            System.out.println(mapToInt(student, t -> t.getAge()));
        }
        System.out.println();

        for (Student student: students) {
            System.out.println(mapToInt(student, t -> t.getScore()));
        }
        System.out.println();

        System.out.printf( "age average = %f\n", average( t -> t.getAge() ));
        System.out.printf( "score average = %f\n", average( t -> t.getScore() ));
    }
}



java.util.function.Operator

  • 매개O, 리턴O
  • 동일한 타입의 인자를 연산 하는데 많이 사용
public class OperatorExample {
    private static int[] ints = { 92, 95, 97, 100, 85, -100, -200, 300 };

    // 1. 인터페이스의 인스턴스 생성
    public static int maxOrMin(IntBinaryOperator operator) {
        int res = ints[0];

        for (int i: ints) {
            // 2. 추상 메서드 호출
            res = operator.applyAsInt(res, i); // 갱신
        }
        return res;
    }


    public static void main(String[] args) {
        // 3. 인터페이스의 추상 메서드 구현
//        int max = maxOrMin((a, b) -> Math.max(a, b));
        int max = maxOrMin(Math :: max);
        System.out.println("max = " + max);

//        int min = maxOrMin((a, b) -> Math.min(a, b));
        int min = maxOrMin(Math :: min);
        System.out.println("min = " + min);

    }
}



java.util.function.Predicate

  • boolean test(T t);
    • selsect * from student where age >= 20;
    • 특정 조건 만족하는 애들만 가져와서 컬렉션에 담기 => 특정 조건을 작성하는 Predicate
    • 인자가 특정 조건을 만족 하는지에 따라 boolean 반환 ('특정조건'을 람다로 구현)
public class PredicateExample {
    private static List<Student> students = Arrays.asList(
            new Student("kim", 20, 80),
            new Student("dong", 19, 60),
            new Student("kil", 17, 70),
            new Student("yoon", 16, 100),
            new Student("lee", 25, 50),
            new Student("park", 29, 100),
            new Student("choi", 39, 70),
            new Student("kang", 31, 80),
            new Student("lim", 32, 60)
    );

    // 1. 인터페이스의 인스턴스 생성
    public static double averageIf(Predicate<Student> predicate) {
        int count = 0;
        int sum = 0;

        for ( Student student: students ) {
            // 2. 추상 메서드 호출 + 인자 전달
            if ( predicate.test(student) ) { // 특정 조건의 student만 모아서
                sum += student.getScore(); // 평균 구하기
                count++;
            }
        }

        return (double) sum / count;
    }

    public static void main(String[] args) {
        // 3. 추상 메서드 구현
        double average30 = averageIf( t -> t.getAge() >= 30 );
        // student에서 30살 이상인 애들만 인자로 전달되어 평균 구해짐 (30살 이상 학생의 평균)

        System.out.println("average30 = " + average30);

        double average20 = averageIf( t -> t.getAge() < 20 );
        System.out.println("average20 = " + average20);
    }
}



메서드::참조

  • 인터페이스에 있는 파라미터 정보를 람다식에서 생략
  • 클래스 :: 정적 메소드
  • 인스턴스 변수 :: 메소드
  • 클래스 :: new



람다식의 외부 참조 (자바의 Multi Thread 환경 동시성 제어)

https://jaehoney.tistory.com/112
https://steady-coding.tistory.com/568

  1. volatile
  • 각 스레드가 캐시에 저장된 데이터 아닌 메인 메모리에 접근해서 기존의 데이터 읽고 write
  • 멀티 스레드 환경에서 완전히 안전하지 않음
  1. synchronized
  • 너무 느림
  1. Automic variable
  • CAS(compare-and-swap) 알고리즘 이용
  • 람다 스레드가 가지고 있는 값이 메인 스레드의 현재 값과 같은 지 비교하고, 같으면 그냥 사용하고 다르면 현재의 값을 받아옴
  • private volatile int value; : volatile은 메인 메모리(스레드 아닌)에서 값을 읽음
private static AtomicLong number = new AtomicLong(0); // 원래 값이 

public static synchronized void increase() {
    number.set(number.get() + 1); // 수정 되었으므로 
    System.out.println(number); // 수정된 값을 가져옴
}

0개의 댓글

관련 채용 정보