👍 무엇을 새롭게 알았는지
제네릭(Generic)
- 제네릭은 클래스, 메서드 등에 사용되는 <T> 타입 매개변수를 의미
- 실제 데이터 타입으로 대체되어 활용
- 타입을 미리 지정하지 않고 사용 시점에 유연하게 결정할 수 있는 문법
- 코드 재사용성과 타입 안정성을 보장받을 수 있는 장점을 갖고 있음
⚠️ 단, 과도하게 사용 시 오히려 복잡해질 수 있으므로 주의가 필요함 - 제네릭 클래스
- 클래스 선언부에 <T> 가 선언된 클래스
- 타입 매개변수를 사용해 다양한 데이터 타입을 안전하게 처리할 수 있는 구조
- GenericBox<T>를 활용해서 String, Integer, Double 등 다양한 타입 저장 가능
💡타입소거(Erasure)- 타입 소거는 컴파일 시점에 제네릭 타입 정보를 제거하는 과정
- <T> 타입 매개변수 부분은 Object로 대체됨
- 필요한 경우 컴파일러가 자동으로 강제 다운 캐스팅 코드를 삽입하여 타입 안전성을 보장
- 제네릭 메서드
- 제네릭 메서드는 메서드 선언부에 <T> 가 선언된 메서드
- 제네릭 메서드는 클래스 제네릭 타입과 별개로 독립적인 타입 매개변수를 가짐
아래의 예시 코드를 통해 제네릭클래스 타입과 제네릭 메서드의 타입 차이를 인지하고 넘어갈 수 있어야 함
public class Main {
public static void main(String[] args) {
GenericBox<String> strGBox = new GenericBox<>("ABC");
GenericBox<Integer> intGBox = new GenericBox<>(100);
// ⚠️ 일반메서드: 클래스 타입 매개변수를 따라갑니다.
// String 데이터 타입 기반으로 타입소거가 발생.
// String 타입의 다운캐스팅 코드 삽입!
strGBox.printItem("ABC"); // ✅ String 만 사용가능
strGBox.printItem(100); // ❌ 에러 발생
// ✅ 제네릭 메서드: 독립적인 타입 매개변수를 가집니다.
// String 타입 정보가 제네릭 메서드에 아무런 영향을 주지 못함.
// 다운캐스팅 코드 삽입되지 않음.
strGBox.printBoxItem("ABC"); //✅ 모든 데이터 타입 활용 가능
strGBox.printBoxItem(100); //✅ 모든 데이터 타입 활용 가능
strGBox.printBoxItem(0.1); //✅ 모든 데이터 타입 활용 가능
}
}
람다(Lambda)
- 람다의 개념을 학습하기 전 익명 클래스에 대해 먼저 알고 넘어가보자
- 익명 클래스 ❓
- 이름이 없는 클래스를 익명 클래스라고 함
- 별도의 클래스 파일을 만들지 않고 코드 내에서 일회성으로 정의해 사용하기 때문에 이름이 없다고 부름
- 인터페이스, 클래스(일반,추상)의 구현과 상속을 활용해 익명 클래스를 구현할 수 있음
→ 람다에서는 인터페이스를 사용한 익명 클래스가 활용됨
- 익명 클래스 ❓
- 람다는 익명 클래스를 더 간결하게 표현하는 문법
- 함수형 인터페이스를 통해서 구현하는 것을 권장
→ 하나의 추상 메서드만 가져야 하기 때문
→ 하지만, 하나의 추상 메서드를 가진 일반 인터페이스를 통해서도 사용
- 함수형 인터페이스를 통해서 구현하는 것을 권장
⚠️ 람다식 활용시에는 꼭 함수형 인터페이스를 활용할 것
- 함수형 인터페이스는 단 하나의 추상 메서드만 가지도록 강제하는 어노테이션
- 람다식에서는 함수형 인터페이스가 활용됨
- 인터페이스에 두 개 이상의 추상 메서드가 존재하면 컴파일러가 어떤 메서드를 구현해야 하는지 모호해지기 때문
- Ex) 오버로딩 기능을 가진 sum() 메서드를 람다 포현식으로 구현하려면 명확하지 않은 부분으로 인해 모호성이 발생할 수 있음
- 람다식을 매개변수로 전달하는 방법은 3가지로 확인해볼 수 있음.
- 익명클래스를 변수에 담아 전달
public class Main {
public static int calculate(int a, int b, Calculator calculator) {
return calculator.sum(a, b);
}
public static void main(String[] args) {
Calculator cal1 = new Calculator() {
@Override
public int sum(int a, int b) {
return a + b;
}
};
// ✅ 익명 클래스를 변수에 담아 전달
int ret3 = calculate(3, 3, cal1);
System.out.println("ret3 = " + ret3); // 출력: ret3 = 6
}
}
- 람다식을 변수에 담아 매개변수로 전달
public class Main {
public static int calculate(int a, int b, Calculator calculator) {
return calculator.sum(a, b);
}
public static void main(String[] args) {
Calculator cal2 = (a, b) -> a + b;
// ✅ 람다식을 변수에 담아 전달
int ret4 = calculate(4, 4, cal2);
System.out.println("ret4 = " + ret4); // 출력: ret4 = 8
}
}
- 람다식을 직접 매개변수로 전달
public class Main {
public static int calculate(int a, int b, Calculator calculator) {
return calculator.sum(a, b);
}
public static void main(String[] args) {
// ✅ 람다식을 직접 매개변수로 전달
int ret5 = calculate(5, 5, (a, b) -> a + b);
System.out.println("ret5 = " + ret5); // 출력: ret5 = 10
}
}
스트림(Stream)
- 데이터를 효율적으로 처리할 수 있는 흐름을 의미
- 선언형 스타일로 가독성이 굉장히 뛰어남
- 데이터 준비 → 중간 연산 → 최종 연산 순으로 처리
- 중간연산은 한번이 아닌 여러번 사용할 수 있음 (주로, filter + map 을 함께 사용함)
- 스트림은 컬렉션(List, Set 등)과 함께 자주 활용됨
| 단계 | 설명 | 주요 API |
| 1. 데이터 준비 | 컬렉션을 스트림으로 변환 | stream(), parallelStream() |
| 2. 중간 연산 등록 (즉시 실행되지 않음) |
데이터 변환 및 필터링 | map(), filter(), sorted() |
| 3. 최종 연산 | 최종 처리 및 데이터 변환 | collect(), forEach(), count() |
- 예시 코드로 구현된 람다식 표현을 통해 3가지 진행 절차를 구분지어 확인할 수 있음
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<Integer> arr = new ArrayList<>(List.of(1, 2, 3, 4, 5, 6, 7, 8, 9));
// filter : 익명 객체로 구현
Predicate<Integer> predicate = new Predicate<>() {
@Override
public boolean test(Integer integer) {
return integer % 2 == 0;
}
};
// filter : 람다로 구현
List<Integer> filteredArr = arr.stream()
.filter(integer -> integer % 2 == 0)
.collect(Collectors.toList());
System.out.println("filteredArr = " + filteredArr);
}
}
쓰레드(Thread)
- 쓰레드는 프로그램 내에서 독립적으로 실행되는 하나의 작업 단위를 말함 (한 명의 일꾼)
- 싱글 쓰레드는 한 번에 하나의 작업만 처리하지만 멀티쓰레드는 여러 작업을 동시에 처리할 수 있음
- 싱글 쓰레드는 여러 개의 작업이 있다면 순차적으로 처리가 되어야 함
- 멀티 쓰레드를 활용하면 여러 작업을 병렬로 수행할 수 있어 처리 성능을 향상시킬 수 있음
- Thread 클래스를 상속받아 쓰레드를 구현할 수 있음
- Thread.run() 메서드를 오버라이드해서 쓰레드가 수행할 작업을 정의할 수 있음
- start() 메서드를 호출하면 새로운 쓰레드가 생성되고 run() 의 작업내용이 실햄됨
- join() - 특정 쓰레드가 끝날 때까지 기다리기
- 호출한 쓰레드가 끝날 때까지 main() 쓰레드가 대기함
- 이를 응용하면 쓰레드가 총 작업 완료 시간을 측정할 수 있게 만들 수 있음
public class Main {
public static void main(String[] args) {
System.out.println("::: main 쓰레드 시작 :::");
Thread thread0 = new MyThread();
Thread thread1 = new MyThread();
long startTime = System.currentTimeMillis();
// 1. thread0 시작
System.out.println("thread0 시작");
thread0.start();
// 2. thread1 시작
System.out.println("thread1 시작");
thread1.start();
// main 쓰레드를 대기 시키기
try {
thread0.join();
thread1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
System.out.println("작업 소요 시간 : " + totalTime + "ms");
System.out.println("::: main 쓰레드 종료 :::");
}
}
- Runnable 인터페이스 활용 (권장)
- 권장하는 주요 요인
- 유지 보수성과 재사용성 향상
- 확장 가능성
- 기존 클래스를 유지하면서 확장 가능
Java는 클래스의 다중 상속을 지원하지 않음
Thread 를 상속하면 다른 클래스를 상속할 수 없어 확장성이 떨어지게 됨 - Runnable은 인터페이스이므로 기존 클래스의 기능을 유지하면서 상속을 통해 확장할 수 있음
- 기존 클래스를 유지하면서 확장 가능
- 권장하는 주요 요인
- MyNewClass 라는 클래스를 만들어 상속하고 싶은 상황일 때
public class MyThread extends Thread, MyNewClass{ // ❌ 다중 상속 불가
...
}
public class MyRunnable extends MyNewClass implements Runnable { // ✅ 다중 상속
@Override
public void run() {
String threadName = Thread.currentThread().getName();
for (int i = 0; i < 10; i++) {
System.out.println("현재 쓰레드: " + threadName + " - " + i);
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
MyRunnable task = new MyRunnable();
// ✅ 기존 클래스를 유지하면서 확장해서 활용
task.printMessage();
Thread thread0 = new Thread(task);
Thread thread1 = new Thread(task);
thread0.start();
thread1.start();
}
}
'JAVA' 카테고리의 다른 글
| [JAVA] JSP vs Thymleaf 개념 더 알아보기 (0) | 2025.09.19 |
|---|---|
| [JAVA] Enum이란? (0) | 2025.09.18 |
| [JAVA] 자바 개념 확장 - Exception, Optional, Colletion (0) | 2025.09.17 |
| JAVA 객체지향 개념 정리 (1) | 2025.09.16 |
| JAVA 문법 규칙 및 GitHub 커밋 컨벤션 학습 (0) | 2025.09.15 |