backend
  • README
  • DOCS
    • Java Docs
    • Servlet Docs
    • JSP Docs
    • DB & SQL Docs
    • Spring Boot Docs
    • Spring Security Docs
    • AWS Docs
  • 설치하기
    • Intellij 설정
  • 자바
    • 01 Java란?
    • 02 자바 시작하기
    • 03 자료형과 연산자
    • 04 제어문
    • 05 메소드
    • 06 클래스 기초
      • Static 보충자료
      • 패키지 보충자료
    • 07 객체지향 프로그래밍
    • 08 클래스 더 알아보기
      • 열거형 ENUM 보충자료
    • 09 클래스와 자료형
      • 다형성 보충자료
      • 제네릭 보충자료
    • 10 컬렉션 프레임워크
      • 컬렉션 프레임워크 보충자료
    • 11 람다식과 함수형 프로그래밍
      • 람다식 보충자료
    • 12 오류 대비하기
      • 오류 보충자료
    • 13 멀티태스킹
      • 멀티태스킹 보충자료
    • 교재보충
      • java.lang
  • 스프링
    • 서블릿, JSP
      • 05 Servlet(서블릿)
        • 서블릿 보충자료
        • 서블릿 추가코드
        • XML, YAML, JSON
      • 06 JSP(자바 서버 페이지)
        • JSP 보충자료
      • 07 JSTL(JSP 스탠다드 태그 라이브러리)
        • JSTL 보충자료
      • 08 Cookie(쿠키), Session(세션)
      • 09 서블릿,필터,리스너
        • 서블릿,필터,리스너 보충자료
      • 11 도서관리 프로젝트 실습
    • Spring Boot
      • 01 스프링 등장 배경, 객체지향
        • 스프링 등장 배경, 객체지향 보충자료
      • 02 IOC(제어의 역전), DI(의존성 주입)
        • IOC 보충자료
        • DI 보충자료
      • 03 스프링 구조
        • 스프링 구조 보충설명
      • 04 테스트코드 실습
      • 05 스프링 빈 설정
        • 스프링 빈 설정 보충자료
      • 06 싱글톤
        • 싱글톤 보충 자료
      • 07 스프링 빈 자동설정
        • 스프링 빈 자동설정 보충자료
      • 08 빈 생명주기
        • 빈 생명주기 보충자료
      • 09 빈 스코프
        • 빈 스코프 보충자료
      • 10 스프링 MVC
        • 스프링 MVC 보충자료
        • 데이터베이스 연동에 필요한 부분
      • 11 Validation(검증)
        • Validation(검증) 보충자료
      • 12 Bean Validation(빈검증)
        • Bean Validation(빈검증) 보충자료
      • 13 예외처리
        • 예외처리 보충자료
      • 14 타입변환
      • 15 JDBC(Java Database Connectivity)
      • 16 커넥션풀
      • 17 트랜잭션
        • 트랜잭션 보충자료
      • 18 JDBC 템플릿 활용
      • 19 MyBatis
      • 20 JPA(Java Persistence API)
      • 22 게시판 프로젝트 실습
    • Spring Security
      • 보안(Security)
      • Spring Security
      • 2. Spring Security 알아보기
        • 보안 위협 실제 사례와 방어 전략
      • 3. Spring Security 기본 동작 흐름
      • 4. Spring Security로 인증 권한 추가하기
        • Spring Security의 인증 및 인가
      • 5. Spring Security에서 세션 관리하기
        • 세션(Session)과 쿠키(Cookie) 비교, 토큰(Token)과의 관계
        • 해싱 및 해싱알고리즘
        • base64
      • 6. Spring Security 악용 보호
        • SameSite
      • 7. Spring Security로 인가 권한 추가하기
      • 8. Bcrypt(비크립트) 암호화
      • OAuth2 적용하기
  • 네트워크
    • HTTP
    • OSI 7계층
  • DB&SQL
    • 01 Database(데이터베이스)와 SQL 개요
    • 02 관계형 모델
    • 03 집합
    • 04 JOIN 연산
    • 05 MySQL
      • 세이브포인트
      • DBeaver, Mysql 오토커밋 설정 관련
    • 06 SQL 기초
      • 예시데이터 쿼리문
    • 07 SQL 실습
      • 실습 스키마
    • 08 Join 활용
      • 실습스키마
    • 09 SQL 활용
      • 실습스키마
    • 10 정규화
      • 실습 스키마
    • 데이터타입
    • 예시 프로젝트 스키마 구성
  • AWS
    • SSL 연결하기
    • 보충설명
Powered by GitBook
On this page
  • 제네릭이 필요한 이유
  • 제네릭의 기본 특징
  • 2. 타입 매개변수 제한
  • 3. 제네릭 메서드
  • 4. 제네릭 클래스와 제네릭 메서드의 차이
  • 5. 정리
  • 와일드카드 (Wildcard)
  • 1. 와일드카드란?
  • 2. 와일드카드와 제네릭의 차이점
  • 3. 와일드카드의 타입 제한
  • 4. 와일드카드의 특징과 주의점
  • 타입 이레이저 (Type Erasure)
  • 1. 타입 이레이저란?
  • 2. 타입 이레이저의 동작
  • 3. 타입 이레이저의 주의점
  • 4. 예제: 타입 이레이저 동작
  • 5. 정리
  1. 자바
  2. 09 클래스와 자료형

제네릭 보충자료

제네릭이 필요한 이유

(1) 동일한 기능, 다른 타입

  • 동일한 로직을 처리하지만, 타입이 다를 경우 매번 새로운 메서드를 생성해야 합니다.

문제점

class Printer {
    void printString(String str) {
        System.out.println("String: " + str);
    }

    void printInteger(Integer num) {
        System.out.println("Integer: " + num);
    }
}
  • 위 코드처럼 각 타입별로 메서드를 정의하면 중복 코드가 발생합니다.

(2) 다형성으로 해결

  • Object 타입을 사용하면 다형성을 이용하여 타입을 처리할 수 있습니다.

문제점

class Printer {
    void printObject(Object obj) {
        System.out.println("Object: " + obj);
    }
}

public class GenericExample {
    public static void main(String[] args) {
        Printer printer = new Printer();
        printer.printObject("Hello"); // 정상 실행
        printer.printObject(123);    // 정상 실행

        // 잘못된 타입으로 캐스팅
        String result = (String) printer.printObject(123); // 런타임 오류 발생 가능
    }
}
  • 타입 안정성 부족: 잘못된 타입을 캐스팅할 경우 런타임 오류가 발생할 수 있습니다.

  • 컴파일 단계에서 이러한 오류를 잡아낼 수 없습니다.

(3) 제네릭으로 해결

  • 제네릭을 사용하면 컴파일 시 타입을 체크하여 런타임 오류를 방지할 수 있습니다.

  • 코드 재사용성과 타입 안정성을 동시에 보장합니다.

제네릭을 사용한 해결 코드

// 제네릭 클래스를 사용
class Printer<T> { // T는 타입 매개변수
    void print(T obj) {
        System.out.println("Value: " + obj);
    }
}

public class GenericSolutionExample {
    public static void main(String[] args) {
        // String 타입 처리
        Printer<String> stringPrinter = new Printer<>();
        stringPrinter.print("Hello, Generics!");

        // Integer 타입 처리
        Printer<Integer> integerPrinter = new Printer<>();
        integerPrinter.print(123);

        // Double 타입 처리
        Printer<Double> doublePrinter = new Printer<>();
        doublePrinter.print(3.14);
    }
}

실행 결과

Value: Hello, Generics!
Value: 123
Value: 3.14

제네릭의 기본 특징

(1) 기본형은 제네릭 타입으로 사용할 수 없다

  • 제네릭은 참조형 타입만 사용 가능합니다.

  • 기본형 데이터 타입(int, double 등)을 사용하려면 래퍼 클래스(Integer, Double)로 변환해야 합니다.

코드 예시

class Box<T> {
    private T value;

    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}

public class GenericPrimitiveExample {
    public static void main(String[] args) {
        // 기본형 사용 불가: Box<int> box = new Box<>(); // 컴파일 오류
        Box<Integer> intBox = new Box<>(); // 래퍼 클래스 사용
        intBox.setValue(123);
        System.out.println(intBox.getValue()); // 123
    }
}

(2) <> 없이 사용 가능 (원시 타입, Raw Type)

  • 제네릭 타입을 선언하지 않으면 Object 타입으로 처리됩니다.

  • 문제점:

    • 타입 안정성이 보장되지 않아, 잘못된 타입 사용 시 런타임 오류가 발생할 수 있습니다.

코드 예시

class RawTypeExample {
    public static void main(String[] args) {
        Box rawBox = new Box(); // 원시 타입 사용
        rawBox.setValue("Hello");
        System.out.println(rawBox.getValue()); // "Hello"

        rawBox.setValue(123); // 다른 타입도 저장 가능
        System.out.println(rawBox.getValue()); // 123 (Object 타입 반환)
    }
}

2. 타입 매개변수 제한

(1) extends로 제한

  • 타입 매개변수를 특정 타입 또는 특정 타입의 하위 클래스만 허용하도록 제한할 수 있습니다.

  • T extends Type:

    • T는 Type 클래스 또는 그 하위 클래스만 허용됩니다.

코드 예시

class NumberBox<T extends Number> {
    private T value;

    public void setValue(T value) {
        this.value = value;
    }

    public T getValue() {
        return value;
    }
}

public class TypeBoundExample {
    public static void main(String[] args) {
        NumberBox<Integer> intBox = new NumberBox<>(); // Integer는 Number의 하위 클래스
        intBox.setValue(100);
        System.out.println(intBox.getValue()); // 100

        NumberBox<Double> doubleBox = new NumberBox<>(); // Double도 허용
        doubleBox.setValue(3.14);
        System.out.println(doubleBox.getValue()); // 3.14

        // NumberBox<String> stringBox = new NumberBox<>(); // 컴파일 오류: String은 Number의 하위 클래스가 아님
    }
}

3. 제네릭 메서드

(1) 정의와 특징

  • 정의:

    • 반환 타입 앞에 **<T>**와 같은 타입 매개변수를 선언하여 제네릭 메서드를 정의합니다.

    • 메서드 호출 시점에 타입이 결정됩니다.

  • 특징:

    • 인스턴스 메서드와 static 메서드 모두 적용 가능합니다.

    • 메서드 내부에서만 타입 매개변수가 적용됩니다.


(2) 예제: 캐스팅 없이 타입 반환

코드 예시

class Util {
    public static <T> T getValue(T value) { // 제네릭 메서드
        return value; // 입력받은 타입 그대로 반환
    }
}

public class GenericMethodExample {
    public static void main(String[] args) {
        String result = Util.getValue("Hello, Generics!"); // 타입 추론
        System.out.println(result); // Hello, Generics!

        Integer number = Util.getValue(123); // 타입 추론
        System.out.println(number); // 123
    }
}

(3) 타입 제한이 있는 제네릭 메서드

코드 예시

class MathUtil {
    public static <T extends Number> double add(T a, T b) { // Number 타입 제한
        return a.doubleValue() + b.doubleValue();
    }
}

public class BoundedGenericMethodExample {
    public static void main(String[] args) {
        System.out.println(MathUtil.add(10, 20)); // 30.0
        System.out.println(MathUtil.add(3.5, 2.5)); // 6.0

        // MathUtil.add("10", "20"); // 컴파일 오류: String은 Number의 하위 클래스가 아님
    }
}

(4) 제네릭 메서드의 타입 추론

  • 컴파일러는 전달된 인자를 기반으로 타입을 추론합니다.

코드 예시

class TypeInference {
    public static <T> void printType(T value) {
        System.out.println("Type: " + value.getClass().getName());
    }
}

public class TypeInferenceExample {
    public static void main(String[] args) {
        TypeInference.printType("Hello"); // String 타입 추론
        TypeInference.printType(123);    // Integer 타입 추론
        TypeInference.printType(3.14);   // Double 타입 추론
    }
}

4. 제네릭 클래스와 제네릭 메서드의 차이

구분

제네릭 클래스

제네릭 메서드

타입 결정 시점

객체를 생성하는 시점

메서드를 호출하는 시점

범위

클래스 전체에서 타입 매개변수 사용 가능

메서드 내부에서만 타입 매개변수 사용 가능

적용 방식

class Box<T>

public static <T> void method(T value)

코드 예시

class GenericClass<T> { // 제네릭 클래스
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

class GenericMethods {
    public static <T> T echo(T value) { // 제네릭 메서드
        return value;
    }
}

public class GenericExample {
    public static void main(String[] args) {
        // 제네릭 클래스
        GenericClass<String> stringBox = new GenericClass<>();
        stringBox.setValue("Hello");
        System.out.println(stringBox.getValue());

        // 제네릭 메서드
        System.out.println(GenericMethods.echo(123)); // 123
        System.out.println(GenericMethods.echo("Generics!")); // Generics!
    }
}

5. 정리

  1. 제네릭 클래스:

    • 객체 생성 시점에 타입을 결정.

    • 동일한 로직을 여러 타입에서 재사용 가능.

  2. 제네릭 메서드:

    • 메서드 호출 시점에 타입을 결정.

    • 캐스팅 없이 입력된 타입 그대로 반환 가능.

  3. 타입 제한:

    • extends 키워드를 사용해 허용된 타입 범위를 설정.

    • 컴파일 단계에서 타입 오류를 방지.

와일드카드 (Wildcard)

1. 와일드카드란?

  • 정의: 와일드카드는 제네릭의 유연성을 높이기 위해 사용되며, **?**로 표현됩니다.

  • 목적:

    • 특정 타입에 한정되지 않고, 여러 타입을 처리할 수 있도록 제네릭을 확장.

    • 이미 만들어진 제네릭 클래스나 메서드를 활용할 때 사용.


import java.util.ArrayList;
import java.util.List;

class Box<T> {
    private T item;

    public void setItem(T item) {
        this.item = item;
    }

    public T getItem() {
        return item;
    }
}

public class WildcardExample {
    // 이미 만들어진 제네릭 클래스를 활용하는 메서드
    public static void printBoxContents(List<? extends Number> boxes) {
        for (Number num : boxes) {
            System.out.println(num);
        }
    }

    public static void main(String[] args) {
        // Box 클래스는 제네릭으로 만들어진 클래스
        Box<Integer> intBox = new Box<>();
        intBox.setItem(100);

        Box<Double> doubleBox = new Box<>();
        doubleBox.setItem(45.67);

        // 제네릭 클래스의 List를 생성
        List<Box<? extends Number>> boxList = new ArrayList<>();
        boxList.add(intBox);
        boxList.add(doubleBox);

        // 와일드카드를 활용한 리스트
        List<Integer> intList = List.of(1, 2, 3, 4, 5);
        List<Double> doubleList = List.of(1.1, 2.2, 3.3);

        // 이미 만들어진 제네릭 메서드를 활용
        System.out.println("Integer List:");
        printBoxContents(intList);

        System.out.println("Double List:");
        printBoxContents(doubleList);
    }
}

코드 설명:

  1. Box<T> 클래스:

    • 제네릭 클래스로, 타입에 따라 item을 저장합니다.

    • 타입 안정성을 제공하며, 타입 캐스팅 없이 사용할 수 있습니다.

  2. printBoxContents 메서드:

    • 와일드카드 <? extends Number>를 사용하여 Number 타입의 하위 클래스만 허용합니다. 즉, Integer, Double, Float 등이 가능합니다.

    • 리스트 안의 값을 출력합니다.

  3. 와일드카드의 유연성:

    • printBoxContents는 타입을 명시적으로 지정하지 않고 Integer와 Double 두 타입 모두를 처리할 수 있습니다.

    • 특정 타입만 허용하려는 경우, 와일드카드로 제한을 설정 (extends 또는 super)할 수 있습니다.

실행 결과:

Integer List:
1
2
3
4
5
Double List:
1.1
2.2
3.3

2. 와일드카드와 제네릭의 차이점

구분

제네릭

와일드카드

사용 대상

새로 정의하는 클래스나 메서드

이미 만들어진 제네릭 클래스나 메서드 활용

표현 방법

타입 매개변수(T, E 등) 사용

? 사용

역할

타입 안정성을 유지하면서 동적인 타입 처리

타입 제한 없이 더 유연한 처리 가능


3. 와일드카드의 타입 제한

(1) 제한 없는 와일드카드 (<?>)

  • 어떤 타입이든 허용.

코드 예시

import java.util.List;

class WildcardExample {
    public static void printList(List<?> list) { // 제한 없는 와일드카드
        for (Object item : list) {
            System.out.println(item);
        }
    }

    public static void main(String[] args) {
        List<String> stringList = List.of("A", "B", "C");
        List<Integer> intList = List.of(1, 2, 3);

        printList(stringList); // 모든 타입 허용
        printList(intList);    // 모든 타입 허용
    }
}

(2) 상한 제한 와일드카드 (<? extends Type>)

  • 와일드카드로 Type 클래스 또는 하위 클래스만 허용.

코드 예시

import java.util.List;

class WildcardWithUpperBound {
    public static void printNumbers(List<? extends Number> list) { // 상한 제한
        for (Number num : list) {
            System.out.println(num);
        }
    }

    public static void main(String[] args) {
        List<Integer> intList = List.of(1, 2, 3);
        List<Double> doubleList = List.of(1.1, 2.2, 3.3);

        printNumbers(intList);    // Integer는 Number의 하위 클래스
        printNumbers(doubleList); // Double도 Number의 하위 클래스
    }
}

(3) 하한 제한 와일드카드 (<? super Type>)

  • 와일드카드로 Type 클래스 또는 상위 클래스만 허용.

코드 예시

import java.util.List;

class WildcardWithLowerBound {
    public static void addNumbers(List<? super Integer> list) { // 하한 제한
        list.add(10); // Integer 추가 가능
        list.add(20);
    }

    public static void main(String[] args) {
        List<Number> numList = new java.util.ArrayList<>();
        addNumbers(numList); // Number는 Integer의 상위 클래스
        System.out.println(numList);
    }
}

4. 와일드카드의 특징과 주의점

  • 동적 타입 반환이 아님:

    • 와일드카드는 반환 타입을 변경하거나 동적으로 처리하지 않습니다.

    • 반환되는 객체는 Object로 처리되거나 명시적 캐스팅이 필요합니다.

  • 제네릭과 함께 사용:

    • 새롭게 정의된 제네릭보다는 이미 만들어진 제네릭 클래스/메서드 활용에 적합.


타입 이레이저 (Type Erasure)

1. 타입 이레이저란?

  • 정의: 자바의 제네릭은 컴파일 시점에만 타입을 체크하며, 컴파일 후에는 모든 제네릭 정보가 제거됩니다.

  • 목적:

    • 하위 호환성을 유지하면서도 타입 안정성을 제공.

  • 특징:

    • 컴파일 후 모든 타입 매개변수(T, E)는 **Object**로 변환.

    • 타입 매개변수가 **extends**로 제한되었을 경우, 제한된 타입으로 변환.


2. 타입 이레이저의 동작

  • 컴파일 전:

    class Box<T> {
        private T value;
        public T getValue() {
            return value;
        }
        public void setValue(T value) {
            this.value = value;
        }
    }
  • 컴파일 후:

    class Box {
        private Object value;
        public Object getValue() {
            return value;
        }
        public void setValue(Object value) {
            this.value = value;
        }
    }

3. 타입 이레이저의 주의점

  • 타입 정보 소실:

    • 제네릭의 타입 매개변수는 컴파일 시점에만 사용되고, 컴파일 이후에는 제거됩니다.

  • T를 사용한 인스턴스 생성 불가:

    • 컴파일 이후 타입 정보가 사라지므로 new T()와 같은 인스턴스 생성은 불가능합니다.

코드 예시: 컴파일 오류

class GenericClass<T> {
    // T value = new T(); // 컴파일 오류: 타입 이레이저로 인해 불가능
}

4. 예제: 타입 이레이저 동작

(1) 제네릭 클래스 컴파일 후 동작

class GenericBox<T> {
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

public class TypeErasureExample {
    public static void main(String[] args) {
        GenericBox<String> stringBox = new GenericBox<>();
        stringBox.setValue("Hello");
        System.out.println(stringBox.getValue()); // "Hello"

        // 컴파일 이후
        // GenericBox의 모든 T가 Object로 변경됨
    }
}

(2) extends를 활용한 타입 제한

코드 예시

class NumberBox<T extends Number> {
    private T value;

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }
}

public class TypeErasureWithBoundsExample {
    public static void main(String[] args) {
        NumberBox<Integer> intBox = new NumberBox<>();
        intBox.setValue(123);
        System.out.println(intBox.getValue()); // 123

        // 컴파일 후, 모든 T는 Number로 변환
    }
}

5. 정리

와일드카드

  1. 와일드카드(?):

    • 제네릭보다 더 유연하게 이미 만들어진 클래스를 활용 가능.

  2. 타입 제한:

    • <? extends Type>: 상한 제한.

    • <? super Type>: 하한 제한.

타입 이레이저

  1. 컴파일 시점에서만 타입 체크:

    • 컴파일 완료 후 제네릭 정보 제거.

    • 모든 타입 매개변수는 Object로 변환.

  2. 주요 특징:

    • extends로 타입 제한 시, 제한된 타입으로 변환.

    • 컴파일 이후 제네릭 타입 정보가 없으므로 new T() 불가.

Previous다형성 보충자료Next10 컬렉션 프레임워크

Last updated 5 months ago