[JAVA] 자바 개념 확장 - Generic, Lambda, Stream, Thread

2025. 9. 17. 19:59·JAVA

 

👍 무엇을 새롭게 알았는지

제네릭(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
'JAVA' 카테고리의 다른 글
  • [JAVA] JSP vs Thymleaf 개념 더 알아보기
  • [JAVA] Enum이란?
  • [JAVA] 자바 개념 확장 - Exception, Optional, Colletion
  • JAVA 객체지향 개념 정리
stark77
stark77
하마의 IT 자기개발 이모저모, 백엔드 개발자로 거듭나기
  • stark77
    하마의 개발자 성장일기
    stark77
  • 전체
    오늘
    어제
    • 분류 전체보기
      • 컴퓨터구조와 운영체제
        • 컴퓨터구조
        • 운영체제
      • SQL 기초
      • Spring
        • 백엔드 기초
        • Spring 실습
      • JAVA
        • Java 실습
      • HTML&CSS
        • HTML&CSS 실습
      • Git&GitHub
        • Git&GitHub 실습
      • 내배캠 끄적끄적
        • Today I Learned
      • 유용한 툴 및 사이트 정리
      • 취미
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    객체지향프로그래밍
    경합조건과 교착상태
    JPA
    RestTemplate
    SpringSecurity
    Java 문법기초
    Github
    백엔드 기초
    git
    String.format
    실시간 데이터 처리
    jsp
    다형성
    thymleaf
    WebSocket
    웹소켓
    네트워크 기초
    프로세스와 쓰레드
    java
    객체지향
    백엔드 기초다지기
    BEAN
    algorithm
    Stomp
    Spriingboot
    for문
    Spring
    MVC
    HTML&CSS
    Til
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
stark77
[JAVA] 자바 개념 확장 - Generic, Lambda, Stream, Thread
상단으로

티스토리툴바