JAVA

자바 기초 문법 다지기 4

도원좀비 2025. 2. 26. 18:02

제네릭(Generic)이란?

  • 제네릭은 클래스, 메서드 등에 사용되는 <T>타입 매개변수를 의미
  • 타입을 미리 지정하지 않고 사용 시점에 유연하게 결정할 수 있는 문법
  • 제네릭을 활용하면 코드 재사용성과 타입 안정성을 보장
  • 하지만 과도하게 사용하면 오히려 복잡해질 수 있으므로 주의
public class GenericBox<T> { // 제네릭 클래스
    private T item;

    public GenericBox(T item) {
        this.item = item;
    }

    public T getItem() {
        return this.item;
    }
}



// 타입소거
public class Main {
    public static void main(String[] args) {
    
        // 1. 재사용 가능(컴파일시 타입소거: T -> Object)
        GenericBox<String> strGBox = new GenericBox<>("ABC");
        GenericBox<Integer> intGBox = new GenericBox<>(100);
        GenericBox<Double> doubleGBox = new GenericBox<>(0.1);

        // 2. 타입 안정성 보장(컴파일시 타입소거: 자동으로 다운캐스팅)
        String strGBoxItem = strGBox.getItem();
        Integer intGBoxItem = intGBox.getItem();
        Double doubleGBoxItem = doubleGBox.getItem();
        System.out.println("strGBoxItem = " + strGBoxItem);
        System.out.println("intGBoxItem = " + intGBoxItem);
        System.out.println("doubleGBoxItem = " + doubleGBoxItem);
    }
}
  • 제네릭 클래스는 클래스 선언부에 <T> 가 선언된 클래스
  • 제네릭 클래스는 클래스 선언시 타입 매개변수를 사용해 다양한 데이터 타입을 안전하게 처리할 수 있는 구조
  • GenericBox<T> 를 활용해서 String, Integer, Double 등 다양한 타입 저장 가능

익명 클래스

  • 이름이 없는 클래스
  • 별도의 클래스 파일을 만들지 않고 코드 내에서 일회성으로 정의해 사용하기 때문에 이름이 없다고 부름
  • 인터페이스, 클래스(일반, 추상)의 구현과 상속을 활용해 익명 클래스를 구현 가능→ 람다에서는 인터페이스를 사용한 익명 클래스가 활용됩니다.
public interface Calculator {

    int sum(int a, int b);
}

public class Main {

    public static void main(String[] args) {
    
		    // 익명 클래스 활용
        Calculator calculator1 = new Calculator() {
            @Override
            public int sum(int a, int b) {
                return a + b;
            }
        };
        
        int ret1 = calculator1.sum(1, 1);
        System.out.println("ret1 = " + ret1);
    }
}

 

 

람다(Lambda)

  • 람다는 익명 클래스를 더 간결하게 표현하는 문법
  • 함수형 인터페이스 를 통해서 구현하는 것을 권장
// 람다 표현식
Calculator calculator1 = (a, b) -> a + b;

// 익명클래스
Calculator calculator1 = new Calculator() {
		@Override
		public int sum(int a, int b) {
				return a + b;
		}
};

@FunctionalInterface // 함수형 인터페이스 선언
public interface Calculator {

    int sum(int a, int b); // 오직 하나의 추상 메서드만 선언해야합니다.
}

public class Main {

    public static void main(String[] args) {
    
		    ...

        // 람다식 활용
        Calculator calculator2 = (a, b) -> a + b;
        int ret2 = calculator2.sum(2, 2);
        System.out.println("ret2 = " + ret2);
    }
}
  • 람다식 활용 때는 반드시 함수형 인터페이스를 활용

스트림(stream) 이란?

  • 스트림은 데이터를 효율적으로 처리할 수 있는 흐름
  • 선언형 스타일로 가독성이 좋음
  • 데이터 준비 → 중간 연산 → 최종 연산 순으로 처리
  • 스트림은 컬렉션(List, Set 등)과 함께 자주 활용

for vs 스트림

public class Main {

    public static void main(String[] args) {

        // ArrayList 선언
        List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

        // for 명령형 스타일: 각 요소 * 10 처리
        List<Integer> ret1 = new ArrayList<>();
        for (Integer num : arrayList) {
            int multipliedNum = num * 10; // 각 요소 * 10
            ret1.add(multipliedNum);
        }
        System.out.println("ret1 = " + ret1); 
    }
}
public class Main {

    public static void main(String[] args) {

        // ArrayList 선언
        List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

        //  스트림 선언적 스타일: 각 요소 * 10 처리
        List<Integer> ret2 = arrayList.stream().map(num -> num * 10).collect(Collectors.toList());
        System.out.println("ret2 = " + ret2);
    }
}
단계 설명 주요 API
1. 데이터 준비 컬렉션을 스트림으로 변환 stream(), parallelStream()
2. 중간 연산 등록 데이터 변환 및 필터링 map(), filter(), sorted()
3. 최종 연산 최종 처리 및 데이터 반환 collet(), forEach(), count()
arrayList
	.stream()  // 1. 데이터 준비
	.map()     // 2. 중간 연산 등록
	.collect() // 3. 최종 연산
// 1. 데이터 준비: 스트림 생성
Stream<Integer> stream = arrayList.stream();

// 2. 중간 연산 등록: 각 요소를 10배로 변환 로직 등록
Stream<Integer> mappedStream = stream.map(num -> num * 10);

// 3. 최종 연산: 최종 결과 리스트로 변환
List<Integer> ret2 = mappedStream.collect(Collectors.toList());

// 한 줄로 표현 가능
List<Integer> ret2 = arrayList.stream() // 1. 데이터 준비
    .map(num -> num * 10)               // 2. 중간 연산 등록
    .collect(Collectors.toList());  // 3. 최종 연산

 

 

스트림과 람다식 활용

public class Main {

    public static void main(String[] args) {

        // ArrayList 선언
        List<Integer> arrayList = new ArrayList<>(List.of(1, 2, 3, 4, 5));

        // 스트림 없이: 각 요소 * 10 처리
        ArrayList<Integer> ret1 = new ArrayList<>();
        for (Integer num : arrayList) {
            int multipliedNum = num * 10;
            ret1.add(multipliedNum);
        }
        System.out.println("ret1 = " + ret1);

        // 스트림 활용: 각 요소 * 10 처리
        List<Integer> ret2 = arrayList.stream().map(num -> num * 10).collect(Collectors.toList());
        System.out.println("ret2 = " + ret2);

        // 직접 map() 활용해보기
        // 1. 익명클래스를 변수에 담아 전달
        Function<Integer, Integer> function = new Function<>() {

            @Override
            public Integer apply(Integer integer) {
                return integer * 10;
            }
        };
        List<Integer> ret3 = arrayList.stream()
                .map(function)
                .collect(Collectors.toList());
        System.out.println("ret3 = " + ret3);

        // 2. 람다식을 변수에 담아 전달
        Function<Integer, Integer> functionLambda = (num -> num * 10);
        List<Integer> ret4 = arrayList.stream()
                .map(functionLambda)
                .collect(Collectors.toList());
        System.out.println("ret4 = " + ret4);
				
				// 람다식 직접 활용
        List<Integer> ret5 = arrayList.stream()
                .map(num -> num * 10)
                .collect(Collectors.toList());
        System.out.println("ret5 = " + ret5);
    }
}

'JAVA' 카테고리의 다른 글

Java Stream API 주요 메서드 정리  (2) 2025.02.28
자바 기본 다지기 최종  (2) 2025.02.27
자바 기초 문법 다지기 3  (1) 2025.02.26
자바 기초 문법 다지기3  (0) 2025.02.26
자바 기초 문법 다지기 2  (1) 2025.02.25