[인프런 워밍업 스터디 클럽 0기 Back] API 작성 3일차 미션

  • 익명 클래스

    • 정의 및 쓰임

      • 이름 없이 선언과 동시에 객체를 생성하는 클래스를 뜻한다.

      • 익명 클래스는 일회용으로 사용되며, 인터페이스나 추상 클래스를 간편하게 구현할 때 사용된다.

      • 따라서, 익명 클래스는 단 한 번만 사용되고 다시는 재사용되지 않는 기능을 구현할 때 유용하다.

    • 사용하는 방법

      • 선언과 객체 생성

        • 익명 클래스는 선언과 객체 생성이 동시에 이뤄진다.

        • new 키워드 뒤에 상속 받는 클래스나 구현할 인터페이스 이름이 온다.

        • 그 뒤에 클래스 본문이 중괄호 안에 정의된다.

      • 구현

        • 익명 클래스는 상속ㄱ받은 클래스나 구현한 인터페이스의 메서드를 오버라이드(재정의)하여 상요할 수 있다.

        • 익명 클래스 내부에서만 사용되는 로컬 변수에 접근할 때는 해당 변수가 final 인 경우에만 접근이 가능하다.

      • 사용 범위

        • 익명 클래스는 선언된 위치에서만 사용할 수 있다.

        • 코드의 가독성을 높이고 코드를 간결하게 만드는데 도움을 준다.

      package com.example.springlibraryapp.controller.calculator;
      
      import java.util.concurrent.ExecutorService;
      import java.util.concurrent.Executors;
      import java.util.concurrent.Future;
      
      public class AnonymousClassExample {
          public static void main(String[] args) throws Exception {
              ExecutorService executor = Executors.newSingleThreadExecutor();
              
              Future<?> future = executor.submit(new Runnable() {
                  @Override
                  public void run() {
                      System.out.println("비동기 작업 실행");
                  }
              });
              
              future.get();
              
              System.out.println("비동기 작업 완료");
              executor.shutdown();
          }
      }
      
  • 람다

    • 의의 및 효과

      • 간결한 방식으로 익명 메서드를 표현할 수 있다.

      • 코드의 길이를 줄이고, 가독성을 향상시킨다.

      • 람다식은 주로 함수형 인터페이스의 인스턴스를 생성할 때 사용된다.

      • 함수형 인터페이스란 추상 메서드를 단 하나만 가지고 있는 인터페이스이다.

    • 람다식의 기본 문법

      • 매개변수 목록

        • 메서드 매개변수 목록을 나타내며, 괄호 안에 정의된다.

        • 매개변수의 타입을 명시할 수 있지만, 컴파일러가 문맥을 통해 유추할 수 있기 때문에 생략할 수 있다.

      • 화살표 : 이것을 통해 매개변수와 실행문을 구분한다.

      • 실행문

        • 람다식의 본체이며, 실제 수행될 코드를 나타낸다.

        • 단일 실행문만 있을 경우 중괄호를 생략할 수 있고, 실행 결과가 자동으로 반환된다.

        • 실행문이 둘 이상이거나, 반환값이 있으면 중괄호를 써야한다.

      (매개변수 목록) -> {실행문}
      
    • 람다식 예시

      () -> System.out.println("Hello, Lambda");
      
      name -> Systeom.out.println("Hello, " + name);
      
      (int a, int b) -> {return a + b;};
      
      (String s) -> {return s.toUpperCase();};
      
      List<String> list = Arrays.asList("apple", "banana", "cherry");
      list.forEach(element -> System.out.println(element));
      
  • 함수형 프로그래밍

    • 프로그래밍의 패러다임이며, 계산을 수학적 함수 평가로 간주하고 상태 변경이나 가변 데이터를 피하는 스타일이다.

    • 특징

      • 불변성

        • 데이터가 한 번 생성되면 변경되지 않는다.

        • 함수형 프로그래밍에서는 데이터를 변경하는 대신, 변경된 데이터의 새로운 복사본을 생성해서 사용한다.

      • 함수의 일급 객체

        • 함수를 변수에 할당하거나 다른 함수의 인자로 전달하고, 함수에서 함수를 반환할 수 있다.

      • 고차 함수

        • 다른 함수를 인자로 받거나 함수를 결과로 반환하는 함수

        • 고차 함수를 통해 코드 재사용성과 추상화 수준을 높일 수 있다.

      • 순수 함수

        • 동일한 인자에 대해 항상 동일한 결과를 반환한다.

        • 외부 상태를 변경하지 않고 외부 상태에 의존하지 않는 함수이다.

        • 순수 함수의 사용은 프로그램의 안정성을 높이고, 테스트와 디버깅을 용이하게 한다.

      • 레이지 평가

        • 필요할 때까지 계산을 지연시키는 기법

        • 불필요한 계산을 줄이고, 성능 최적화에 도움을 준다.

      • 함수 조합

        • 여러 함수를 조합해서 새로운 함수를 만드는 기법.

        • 작은 함수 여러 개를 조합해서 복잡한 기능을 구현할 수 있다.

    • 자바에서 함수형 프로그래밍을 사용하는 이유

      • 간결한 코드 : 람다 표현식과 메서드 참조를 사용하면 코드가 더 간결하고 명확해진다.

      • 병렬 처리 용이성

        • 스트림 API를 사용하면 데이터 컬렉션의 병렬처리가 용이해진다.

        • 멀티코어 프로세서의 이점을 쉽게 활용할 수 있다.

      • 높은 수준의 추상화

        • 함수형 인터페이스와 고차 함수를 통해 코드의 추상화 수준을 높일 수 있다.

        • 코드의 재사용성과 모듈성을 향상시킨다.

  • @FunctionalInterface

    • 의의

      • 해당 인터페이스가 함수형 인터페이스임을 명시하는 어노테이션.

      • 함수형 인터페이스는 추상 메서드를 딱 하나만 가지고 있는 인터페이스이며, 람다 표현식과 함께 사용될 수 있다.

      • 따라서, 컴파일러에게 해당 인터페이스가 함수형 인터페이스의 조건을 충족하는지 검증 요청을 하며, 조건을 충족하지 못하면 컴파일 오류를 발생시킨다.

    • 함수형 인터페이스의 조건

      • 정확히 하나의 추상화 메서드를 가져야한다.

        • 추상 메서드는 람다 표현식의 시그니처(메서드 이름, 입력 매개변수, 반환 타입)을 정의한다.

      • default 메서드나 static 메서드는 여러 개 있어도 된다.

        • 이것들은 구현이 제공되므로 추상 메서드 조건에 영향을 주지 않는다.

      • java.lang.Object 클래스의 public 메서드는 추상 메서드의 개수에 포함되지 않는다.

    • 목적

      • 명시성

        • 인터페이스가 함수형 인터페이스임을 분명하게 한다.

        • 따라서, 다른 개발자들이 이를 쉽게 이해하고 사용할 수 있게 한다.

      • 안정성

        • 컴파일 시점에 인터페이스가 함수형 인터페이스의 조건을 만족하는지 검증한다.

        • 따라서, 실수로 추가된 추상 메서드로 인한 오류를 방지한다.

      • 유지 보수성

        • 프로젝트의 후반부나 다른 개발자가 코드를 수정할 때, 해당 인터페이스가 함수형 인터페이스로 설계되어있음을 알린다.

    • 예제

      • 추상메서드 execute 하나만 가지고 있으므로 함수형 인터페이스이다.

      • @FunctionalInterface 어노테이션을 붙임으로써, 이 인터페이스가 의도적으로 함수형 인터페이스로 설계되었음을 명시한다.

      • 이후 추상 메서드를 추가하려고 하면 컴파일러는 오류를 발생시킨다.

      • 따라서, 해당 어노테이션을 붙여 함수형 인터페이스의 정의를 위반하는 것을 방지함.

      @FunctionalInterface
      public interface SimpleFunctionalInterface {
          void execute(); // 추상 메소드
      
          default void print(String text) {
              System.out.println(text);
          }
      
          static void staticMethod() {
              System.out.println("인터페이스 내의 정적 메소드");
          }
      }
      
  • Stream API

    • 의의와 효과

      • 컬렉션, 배열, I/O 자원 등의 데이터를 함수형 스타일로 처리할 수 있게 하는 기능이다.

      • 데이터를 선언적으로 처리할 수 있으며, 멀티스레딩이나 동시성 문제에 대해 직접적으로 걱정하지 않고도 데이터의 병렬 처리가 가능하다.

      • 코드의 가독성과 작성의 용이성을 크게 향상시킨다.

    • 스트림 API의 특징

      • 불변셩 : 데이터를 변경하지 않는다. 대신, 각 연산은 결과를 포함한 새로운 스트림을 반환한다.

      • 내부 반복 : 컬렉션의 반복을 추상화하여, 반복 방식을 스트림 APi가 내부적으로 처리한다.

      • 연산의 체이닝 : 체인으로 연결할 수 있으며, 더 복잡한 표현식 구성을 가능하게 한다.

      • 지연 실행 : 터미널 연산이 호출될 때까지 실행되지 않는다. → 성능 최적화에 도움

      • 병렬 처리 지원 : 데이터를 자동으로 분할하여 병렬 처리할 수 있는 기능을 제공한다.

    • 주요 연산

      • 중간 연산(intermediate operations)

        • 스트림을 변환하는 연산으로, 하나 이상의 중간 연산을 연결할 수 있으며, 중간 연산은 게으른 방식으로 실행된다.

        • filter(Predicate<T>) : 조건에 맞는 요소만을 포함하는 새로운 스트림을 반환한다.

        • map(Function<T, R>) : 각 요소를 주어진 함수를 사용하여 변환하고 변환된 요소를 포함하는 새 스트림을 반환한다.

        • sorted() : 요소를 자연 순서 또는 Comparator에 따라 정렬한 새 스트립을 반환한다.

      • 터미널 연산(terminal operations)

        • 스트림을 소비하여 결과를 도출하는 연산. 스트림 파이프라인을 실행하고 종료한다.

        • forEach(Consumer<T>) : 스트림의 각 요소에 대해 지정된 작업을 수행한다.

        • collect(Collector<T, A, R>) : 스트림의 요소를 특정 결과 컨테이너에 수집한다.

        • reduce(BinaryOperator<T>) : 스트림의 요소를 조합하여 요약 결과를 생성한다.

    • 스트림API 사용 예시

      • map 중간 연산을 사용하여 해당 리스트의 이름을 대문자로 변환하고, collect 터미널 연산을 통해 결과를 새로운 리스트로 수집한다.

      import java.util.Arrays;
      import java.util.List;
      import java.util.stream.Collectors;
      
      public class Main {
          public static void main(String[] args) {
              List<String> names = Arrays.asList("John", "Jane", "Mary", "Adam", "Diana");
              
              List<String> upperCaseNames = names.stream()
                      .map(String::toUpperCase)
                      .collect(Collectors.toList());
              
              System.out.println(upperCaseNames);
          }
      }
      
    • 결론

      • 스트림 API는 데이터 처리 작업을 더 쉽고 간결하게 만들어주는 강력한 도구이다.

      • 병렬 처리를 통한 성능 최적화뿐만 아니라, 코드의 가독성과 유지 보수성 측면에서도 많은 이점을 제공한다.

  • 메서드 레퍼런스

    • 의의

      • 메서드를 직접 호출하는 대신 사용할 수 있는 구문이다.

      • 메서드 레퍼런스를 사용하면 이미 존재하는 메서드나 생성자를 간결하게 참조할 수 있다.

      • 람다 표현식을 더 단순화시킬 수 있게 하며, 코드 가독성을 향상 시킨다.

    • 기본 구문

      • 정적 메서드 참조

        • 클래스의 정적 메서드를 참조한다.

        • ClassName::methodName

        • ex, String::valueOf, String.valueOf(…)

      • 특정 개체의 인스턴스 메서드 참조

        • 특정 객체의 인스턴스 메서드를 참조한다.

        • instance::methodeName

        • ex, System.out::println, System.outPrintln(…)

      • 임의 개체의 인스턴스 메서드 참조

        • 특정 유형의 모든 개체에 대한 인스턴스 메서드를 참조한다.

        • ClassName::methodName

        • String::toLowerCase 모든 String 객체에 대한 toLowerCase() 메서드의 참조

      • 생성자 참조

        • 클래스의 생성자를 참조한다.

        • ClassName::new

        • ArrayList::new, ArrayList

    • 사용 예시

    public class Main {
        public static void main(String[] args) {
            // 정적 메서드 참조
            Function<String, Integer> parseInt = Integer::parseInt;;
            Integer number = parseInt.apply("100");
            System.out.println(number);
            
            // 특정 개체의 인스턴스 메서드 참조
            List<String> names = new ArrayList<>();
            names.add("James");
            names.add("Emily");
            names.forEach(System.out::println);
            
            // 생성자 참조
            Supplier<List<String>> listSupplier = ArrayList::new;
            List<String> nameList = listSupplier.get();
            nameList.add("David");
            nameList.forEach(System.out::println);
        }
    }
    
  • 자바의 람다식은 왜 등장했을까?

    • 의의

      • 람다식을 통해 함수형 프로그래밍 패러다임을 도입했다.

      • 코드를 더 간결하고 유연하게 만들도록, 개발자들이 멀티코어 프로세싱과 병렬 처리를 더 쉽게 활용할 수 있도록 설계하였다.

    • 람다식의 등장 배경

      • 함수형 프로그래밍의 수요 증가

        • 함수형 프로그래밍 패러다임은 부작용 최소화, 코드의 가독성 향상, 병렬 처리와 비동기 처리 용이 등의 장점을 갖는다.

        • 이러한 패러다임 때문에 수요가 증가하고, 자바 또한 이것을 지원하기 위해 힘썼다.

      • 코드의 간결성과 가독성 향상

        • 익명 클래스를 사용하여 함수형 인터페이스를 구현하는 방식을 사용했다.

        • 이 방법은 코드가 길어지고 가독성이 떨어진다.

        • 람다식을 도입하여 코드를 더 간결하고 읽기 쉽게 만들게 되었다.

      • 병렬 처리 및 멀티코어 활용의 필요성

        • 현대 컴퓨터는 대부분 멀티코어 프로세서를 사용한다.

        • 멀티코어 프로세서의 효율적인 활용을 위해 병렬처리가 필수적이다.

        • 람다식과 스트림 API를 통해 자바는 병렬 처리를 더 쉽게 구현할 수 있게 되었다.

        • 따라서, 성능을 크게 향상시켰다.

      • 자바의 현대화

        • 다른 언어는 이미 함수형 프로그래밍 기능을 제공하고 있었다.

        • 자바 또한 현대 프로그래밍 트랜드에 발밪춰 람다식을 도입.

        • 현대적인 프로그래밍 요구사항을 충족시키기 위한 노력.

    • 람다식의 목적과 기능

      • 익명함수의 제공

        • 람다식을 통해 익명함수를 간단하게 표현할 수 있게 되었다.

        • 컬렉션의 처리, 이벤트 리스너, 비동기 처리에 유용하다.

      • 함수형 인터페이스의 간편한 구현

        • 람다식은 함수형 인터페이스의 인스턴스를 간단하게 생성하는 방법을 제공한다.

        • 인터페이스의 추상 메서드를 구현하는데 필요한 코드 양을 크게 줄일 수 있다.

      • 스트림 API와의 결합

        • 람다식은 스트림 API와 결합하여 컬렉션 처리를 위한 강력한 도구를 제공한다.

        • 데이터의 필터링, 변환, 집계 등을 간결하고 선언적으로 수행할 수 있다.

      • 병렬 처리의 용이성

        • 스트림 API와 람다식을 사용하여 컬렉션의 병렬처리를 간단하게 구현할 수 있다.

        • 멀티코어 프로세서의 이점을 최대한 활용하여, 애플리케이션의 성능을 향상시킬 수 있다.

    • 결론

      • 람다식 도임은 자바 프로그래밍 언어의 큰 변화이다.

      • 자바 개발자들에게 더 많은 표현력과 유연성을 제공한다.

      • 자바는 현대적인 프로그래밍 요구사항을 충족시켰다.

      • 계속해서 발전하는 프로그래밍 언어 생태계에서 중요한 위치를 차지하게 되었다.

  • 람다식과 익명 클래스는 어떤 관계가 있을까? - 람다식의 문법은 어떻게 될까?

    • 의의

      • 자바에서 함수적 인터페이스의 인스턴스를 생성하는 두 가지 방법이라는 공통점이 있다.

      • 함수적 인터페이스란, 하나의 추상 메서드를 가진 인터페이스이다.

      • 두 방법 모두 익명의 내부 클래스를 생성하는 방식이지만, 람다식은 함수형 프로그래밍 개념을 이용하여 익명 클래스보다 코드가 간결하다.

    • 서로의 관계

      • 익명 클래스

        • 자바 초기 버전부터 사용됐다.

        • 인터페이스나 추상 클래스의 추상 메서드를 구현할 때 사용된다.

        • 익명 클래스는 이름이 없으며, 직접 인스턴스화하여 사용한다.

        • 익명 클래스는 코드가 길어지고 가독성이 떨어진다.

      • 람다식

        • 자바 8에서 도입된 기능.

        • 익명 클래스를 대체하는 간결한 방법을 제공한다.

        • 주로 함수형 인터페이스의 구현체를 생성할 때 사용된다.

        • 람다식을 사용하면 보다 간결하고 가독성이 높다.

    • 람다식의 기본 문법

      • 매개변수 목록

        • 메서드 매개변수 목록을 나타내며, 괄호 안에 정의된다.

        • 매개변수의 타입을 명시할 수 있지만, 컴파일러가 문맥을 통해 유추할 수 있기 때문에 생략할 수 있다.

      • 화살표 : 이것을 통해 매개변수와 실행문을 구분한다.

      • 실행문

        • 람다식의 본체이며, 실제 수행될 코드를 나타낸다.

        • 단일 실행문만 있을 경우 중괄호를 생략할 수 있고, 실행 결과가 자동으로 반환된다.

        • 실행문이 둘 이상이거나, 반환값이 있으면 중괄호를 써야한다.

      (매개변수 목록) -> {실행문}
      
      () -> System.out.println("Hello, Lambda");
      
      name -> Systeom.out.println("Hello, " + name);
      
      (int a, int b) -> {return a + b;};
      
      (String s) -> {return s.toUpperCase();};
      
      List<String> list = Arrays.asList("apple", "banana", "cherry");
      list.forEach(element -> System.out.println(element));
      

댓글을 작성해보세요.

채널톡 아이콘