Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags more
Archives
Today
Total
관리 메뉴

요리사에서 IT개발자로

스파르타 부트캠프 Java 제네릭(Generics) 본문

Java

스파르타 부트캠프 Java 제네릭(Generics)

H.S-Backend 2024. 5. 25. 20:08
public class Basket<T> {
    private List<T> items = new ArrayList<>();

    public void addItem(T item) {
        items.add(item);
    }

    public T getItem(int index) {
        return items.get(index);
    }

    public List<T> getAllItems() {
        return items;
    }
}
public class Main {
    public static void main(String[] args) {
        // 사과 바구니
        Basket<Apple> appleBasket = new Basket<>();
        appleBasket.addItem(new Apple());

        // 바나나 바구니
        Basket<Banana> bananaBasket = new Basket<>();
        bananaBasket.addItem(new Banana());
    }

}

// 제네릭 바구니
public class Basket {
    private List<Apple> items = new ArrayList<>();

    public void addItem(Apple item) {
        items.add(item);
    }

    public Apple getItem(int index) {
        return items.get(index);
    }

    public List<Apple> getAllItems() {
        return items;
    }
}
// 사과 바구니
Basket basket = new Basket<>();
basket.addItem(?);

 

첫번째 예시 List<T>와 두번째 예시 List<Apple>

제네릭은 결국 같은 방식으로 동작한다.


 

제네릭(Generics)이란 

클래스나 메소드에서 사용할 데이터 타입을

외부에서 지정할 수 있게 하는 기법이다.

ArrayList<String> list = new ArrayList<>();

위 코드에서 괄호<> 안에 있는 String이 제네릭이다.

// 배열 선언
String[] array = new String[10];

// 리스트 선언
ArrayList<String> stringList = new ArrayList<>();

 

제네릭은 배열의 타입을 지정하듯

리스트 자료형 같은 컬렉션 클래스나 메소드에서 사용할

내부 데이터 타입

외부에서 파라미터처럼 지정하는 기능이다.

 

변수 선언 시 변수의 타입을 지정하듯
제네릭은 객체(Object)에 타입을 지정한다.

Map은 키(Key)와 값(Value)을

제네릭(Generics) 타입으로 정의할 수 있다.

public class Main {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();
        map.put("One", 1);
        map.put("Two", 2);


        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            System.out.println(entry.getKey() + " = " + entry.getValue());
        }
    }
}

 

타입안전성

제네릭을 사용하면 컴파일 시 타입을 검사하여

런타임 에러를 줄일 수 있다.

public class Main {
    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();
        stringList.add("Hello");
        // stringList.add(1); // 컴파일 에러

    }
}

<String> 타입으로 지정되어 있어

add(1) int타입이므로 컴파일 에러가 발생한다.


코드 재사용성

제네릭을 사용하면 다양한 타입을 처리할 수 있는

코드를 작성할 수 있다.

public class Pair<K, V> {
    private K key;
    private V value;

    public Pair(K key, V value) {
        this.key = key;
        this.value = value;
    }

    public K getKey() {
        return key;
    }

    public V getValue() {
        return value;
    }
}

Key key 형식이 아닌 K key로 선언을 함으로써

key 뿐만이 아닌 다른 방식으로 선언이 가능하다.


가독성

제네릭을 사용하면 명시적인 타입 정보를 제공하여

코드의 가독성을 향상 시킬 수 있다.

Box<String> intBox = new Box<>();
intBox.setContent("");
System.out.println(intBox.getContent());

intBox라고 되어있지만

Box<String>이 먼저 보이므로 당연히 String 타입으로 생각하고

setContent의 String 타입을 입력할 것이다.

 


제네릭 타입 파라미터

특정 타입을 상속받거나 구현하도록 제한 할 수 있다.

public class NumberBox<T extends Number> {
    private T number;

    public void setNumber(T number) {
        this.number = number;
    }

    public T getNumber() {
        return number;
    }
}

T는 Number를 상속받고 있어 Number number에도 접근이 가능하다.

그래서

number로 선언하여 set 또는 get을 사용할 수 있는것.


와일드카드는 제네릭타입을 보다 유연하게 사용할 수 있게 한다.

public void printList(List<?> list) {
    for (Object elem : list) {
        System.out.println(elem);
    }
}

// Number와 그 하위 타입만을 받는다
public static void print(List<? extends Number> list) {
    for (Number num : list) {
        System.out.println(num);
    }
}

와일드카드 <?> 는 모든 타입을 허용한다.Ex )Integer, String, Long.......

그래서 향상된 for문 안의 Object 타입으로 선언된 elem에 대입되어 출력이 가능한것.

두번째 는 Number를 상속받고 있어 Number의 하위 클래스 타입만 받을 수 있다.

 


<?>는 와일드 카드이므로 

 어떤 타입으로든 명시하여 사용이 가능하다.

public class WildcardExample {
    public static void printList(List<?> list) {
        for (Object elem : list) {
            System.out.println(elem);
        }
    }

    public static void main(String[] args) {
        List<Object> objList = new ArrayList<>();
        objList.add("String");
        objList.add(1);

        List<Number> numList = new ArrayList<>();
        numList.add(1);
        numList.add(1.5);

        List<Integer> intList = new ArrayList<>();
        intList.add(1);
        intList.add(2);

        printList(objList);  // OK
        printList(numList);  // OK
        printList(intList);  // OK
    }
}
  1. objList는 <Object> 제네릭 타입으로 선언되며 String도 입력이 가능하고 Integer 도 add가 가능하다
  2. numList는 <Number> 제네릭 타입으로 선언되어 Integer와 Float, Double도 add가 가능하다
  3. intList는 <Integer> 제네릭 타입으로 선언되어  int만 add가 가능하다.

<? extends Number>

와일드 카드는 Number를 상속받았기에 그 하위 클래스만 타입만 가능하다.

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

    public class WildcardExample {
    public static void printNumberList(List<? extends Number> list) {
        for (Number num : list) {
            System.out.println(num);
        }
    }

    public static void main(String[] args) {
        List<Object> objList = new ArrayList<>();
        objList.add("String");
        objList.add(1);

        List<Number> numList = new ArrayList<>();
        numList.add(1);
        numList.add(1.5);

        List<Integer> intList = new ArrayList<>();
        intList.add(1);
        intList.add(2);

        // printNumberList(objList);  // Error
        printNumberList(numList);  // OK
        printNumberList(intList);  // OK
    }
}

 

objList는 <Object>제네릭으로 선언되어

Number의 하위 클래스가 아니기에 런타임 에러가 발생한다.


<? super Integer>

와일드 카드는  Integer 상위 클래스 타입만 가능하다(super) 

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

    public class WildcardExample {
    public static void addNumbers(List<? super Integer> list) {
        list.add(1);  // OK
        list.add(2);  // OK
        // list.add(1.5);  // Error
    }

    public static void main(String[] args) {
        List<Object> objList = new ArrayList<>();
        objList.add("String");
        objList.add(1);

        List<Number> numList = new ArrayList<>();
        numList.add(1);
        numList.add(1.5);

        List<Integer> intList = new ArrayList<>();
        intList.add(1);
        intList.add(2);

        addNumbers(objList);  // OK
        addNumbers(numList);  // OK
        // addNumbers(intList);  // Error
    }
}

intList 는 <Integer> 제네릭타입으로 선언되었으므로

상위클래스가 아니기에 런타임 에러가 발생한다.


정리하자면

 

List<?> 

어떤 타입이든 가능하다

List<Object>, List<Number>, List<Integer>....


List<? extends Number> 

List<Number>의 하위타입 가능하다.

List<Object>는 불가능하다.


List<? super Integer> 

List<Integer>와 그 상위타입만 허용된다.

대신 List<Long> 허용되지 않는다.

 

https://www.youtube.com/watch?v=QcXLiwZPnJQ

 

반응형