[인프런 워밍업 스터디 클럽 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));
댓글을 작성해보세요.