제네릭이란 클래스나 메서드를 작성할 때, 다룰 데이터의 타입(Type)을 미리 지정하지 않고, 실제 사용할 때 지정할 수 있도록 하는 기능입니다. List<String>에서 <String> 부분이 바로 제네릭입니다.
왜 사용하는가? (제네릭의 장점)
// 제네릭이 없는 경우
List list = new ArrayList();
list.add("hello");
list.add(123); // 컴파일 시점에는 오류를 잡지 못함
String text = (String) list.get(0); // 정상
String number = (String) list.get(1); // 실행 시 ClassCastException 발생!
// 제네릭을 사용한 경우
List<String> list = new ArrayList<>();
list.add("hello");
// list.add(123); // 컴파일 시점에 바로 오류 발생! (타입 안정성)
String text = list.get(0); // 형 변환이 필요 없음
람다 표현식은 Java 8에서 도입된 혁신적인 기능으로, 이름이 없는 함수(Anonymous Function)를 간결하게 표현하는 방법입니다.
주로 함수형 인터페이스(Functional Interface)를 구현할 때 사용됩니다.
Runnable, Comparator)목적: 불필요한 익명 클래스 코드를 줄여, 코드를 훨씬 더 간결하고 가독성 높게 만듭니다.
// 1. 기존 방식 (익명 클래스)
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Thread is running.");
}
}).start();
// 2. 람다 표현식 적용
// 어차피 Runnable 인터페이스는 run() 메서드 하나 뿐이므로,
// () -> { ... } 부분이 run() 메서드의 구현부임을 컴파일러가 추론함
new Thread(() -> System.out.println("Thread is running.")).start();
(매개변수) -> { 실행문 }for문이나 while문과 같은 반복문을 사용하지 않고, 선언형(Declarative)으로 데이터를 처리할 수 있어 코드가 매우 간결해집니다..stream() 메서드를 호출하여 스트림을 생성합니다.filter(조건): 조건에 맞는 요소만 남김.map(함수): 각 요소를 다른 값으로 변환.sorted(): 요소를 정렬.collect(Collectors.toList()): 스트림을 다시 리스트로 변환.forEach(동작): 각 요소를 순회하며 동작 수행.count(): 요소의 개수를 반환.List<String> names = Arrays.asList("Java", "Python", "JavaScript", "Go");
// 전통적인 for문 방식
List<String> result = new ArrayList<>();
for (String name : names) {
if (name.startsWith("J")) {
result.add(name.toUpperCase());
}
}
// 스트림 API 방식
List<String> streamResult = names.stream() // 1. 스트림 생성
.filter(name -> name.startsWith("J")) // 2. 중간 연산: "J"로 시작하는 이름만 필터링
.map(String::toUpperCase) // 2. 중간 연산: 대문자로 변환
.collect(Collectors.toList()); // 3. 최종 연산: 결과를 리스트로 수집
// streamResult: ["JAVA", "JAVASCRIPT"]
<>)은 컴파일 시점의 타입 안정성을 제공하여, 런타임에 발생할 수 있는 ClassCastException을 예방하고 코드를 깔끔하게 만듭니다.->)은 함수형 인터페이스의 익명 구현체를 매우 간결하게 표현하는 방법으로, 현대 Java 코드의 가독성을 높이는 핵심 요소입니다.for문을 대체하여 컬렉션 데이터를 선언형으로 처리하는 강력한 도구입니다. filter, map과 같은 중간 연산과 collect와 같은 최종 연산을 조합하여 복잡한 데이터 처리를 간결하게 표현할 수 있습니다.