Nick Dev

[JAVA] About 제네릭 본문

Java

[JAVA] About 제네릭

Nick99 2024. 12. 29. 14:51
반응형

https://www.inflearn.com/course/%EA%B9%80%EC%98%81%ED%95%9C%EC%9D%98-%EC%8B%A4%EC%A0%84-%EC%9E%90%EB%B0%94-%EC%A4%91%EA%B8%89-2

김영한님의 '김영한의 실전 자바 - 중급 2편' 강의를 바탕으로 작성된 글입니다.

제네릭이란

  • 타입 안정성을 위해서 사용

  • 제네릭을 쓰면 코드 재사용 + 타입 안정성 모두 보장!!!

사용법

public class GenericBox<T> {  -> 타입 매개변수

    private T value;

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

    public T get() {
        return value;
    }
}

GenericBox<Integer> integerBox = new GenericBox<Integer>(); -> 타입 인자

T : 타입 매개변수

추후에 Integer, String 등 으로 변할 수 있음

  • 타입이 맞지 않으면 컴파일 오류가 발생한다

핵심

  • 제네릭의 핵심은 사용할 타입을 미리 결정하지 않는다는 점이다.
  • 클래스 내부에서 사용하는 타입을 클래스를 정의하는시점에 결정하는 것이 아니라 실제 사용하는 생성 시점에 타입을 결정하는 것이다.
    • 어떤 타입을 쓸지 미래로 미루는것
  • 제네릭 클래스는 타입 매개변수에 타입 인자를 전달해서 사용할 타입을 결정한다.

제네릭 타입, 제네릭 클래스

  • 클래스나 인터페이스를 정의할 때 타입 매개변수를 사용하는 것
public class GenericBox<T> {  -> 타입 매개변수

    private T value;

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

    public T get() {
        return value;
    }
}
  • GenericBox<T> → 제네릭 타입 이라고 함
  • T → 타입 매개변수
    • 실제 타입으로 대체될 거임
  • GenericBox<Integer> → 제네릭 타입 사용할 때 제공되는 실제 타입
    • Integer → 타입 인자

명명 관례

E - Element
K - Key
N - Number
T - Type
V - Value
S,U,V etc. - 2nd, 3rd, 4th types

제네릭 도입 한계

  • T의 타입을 메서드 작성 시점에 알 수 없다.

  • 그래서 Object 기능만 사용 가능하다…

      public class AnimalHospitalV2<T> {
    
          private T animal;
    
          public void set(T animal) {
              this.animal = animal;
          }
    
          public void checkup() {
              // T의 타입을 메서드를 정의하는 시점에는 알 수 없다. Object의 기능만 사용 가능
              animal.toString();
              animal.equals(null);
    
              // 컴파일 오류
              //System.out.println("동물 이름: " + animal.getName());
              //animal.sound();
          }
    
          public T getBigger(T target) {
              // 컴파일 오류
              //return animal.getSize() > target.getSize() ? animal : target;
              return null;
          }
      }
  • 그리고 우린 Animal 과 관련된 타입만 선언해서 사용하게 하고 싶음

    • 오만가지 타입이 다 들어올 수 있음

        public static void main(String[] args) {
            AnimalHospitalV2<Dog> dogHospital = new AnimalHospitalV2<>();
            AnimalHospitalV2<Cat> catHospital = new AnimalHospitalV2<>();
            AnimalHospitalV2<Integer> integerHospital = new AnimalHospitalV2<>();
            AnimalHospitalV2<Object> objectHospital = new AnimalHospitalV2<>();
        }

문제 1. 제네릭에서 타입 매개변수를 사용하면 어떤 타입이든 들어올 수 있다.
문제 2. 타입 매개변수를 어떤 타입이든 수용할 수 있는 Object로 가정하고, Object 의 기능만 사용할 수 있다.

타입 매개변수 제한

  • 타입 매개변수를 특정 타입으로 제한
public class AnimalHospitalV3<T extends Animal> {

    private T animal;

    public void set(T animal) {
        this.animal = animal;
    }

    public void checkup() {
        System.out.println("동물 이름: " + animal.getName());
        System.out.println("동물 크기: " + animal.getSize());
        animal.sound();
    }

    public T getBigger(T target) {
        return animal.getSize() > target.getSize() ? animal : target;
    }

}
  • T는 최소한 Animal이다.
  • 그래서 Animal의 기능들은 사용할 수 있다

문제 해결 1. 타입 안정성 문제 해결
문제 해결 2. 제네릭 도입 시, 어떤 타입이든 들어올 수 있던 문제 해결

  • 제네릭에 타입 매개변수의 상한을 사용해서 타입 안전성을 지키면서 상위 타입의 원하는 기능까지 사용

제네릭 메서드와 와일드 카드

  • 와일드 카드

      static Animal printAndReturnWildcard(Box<? extends Animal> box) {
          Animal animal = box.get();
          System.out.println("이름 = " + animal.getName());
          return animal;
      }
  • 제네릭 메서드

      static <T extends Animal> T printAndReturnGeneric(Box<T> box) {
          T t = box.get();
          System.out.println("이름 = " + t.getName());
          return t;
      }
    • 제네릭이니깐 Box<Dog> dogBox가 들어오면 T = Dog이 되서 리턴 타입도 Dog이 된다
  • 둘 다 해결하는 건 비슷해 보임

  • 하지만 리턴 타입이 다르다

  • 와일드 카드는 Animal로만 리턴되지만, 제네릭 메서드는 입력 받은 타입으로 리턴이 된다

          public static void main(String[] args) {
              Box<Object> objBox = new Box<>();
              Box<Dog> dogBox = new Box<>();
              Box<Cat> catBox = new Box<>();
              dogBox.set(new Dog("멍멍이", 100));
    
              Dog dog = WildcardEx.printAndReturnGeneric(dogBox);
              Animal animal = WildcardEx.printAndReturnWildcard(dogBox);
          }
    • 제네릭 메서드는 매개변수로 dogBox 넣으니깐 리턴 타입이 Dog이 되는거고
    • 와일드 카드는 Animal로 리턴되는 차이가 있다

자바의 제네릭은 단순하게 생각하면 개발자가 직접 캐스팅 하는 코드를 컴파일러가 대신 처리해주는 것

타입 이레이저

  • 컴파일 시점에서 제네릭 확인했으면 제네릭을 모두 지워버리고 타입 캐스팅만 해놓는다

  • 기존 자바 코드와의 호환을 위해서

  • 컴파일 전

      public class AnimalHospitalV3<T extends Animal> {
              private T animal;
    
              public void set(T animal) {
                      this.animal = animal;
              }
    
              public void checkup() {
                      System.out.println("동물 이름: " + animal.getName());
                      System.out.println("동물 크기: " + animal.getSize());
                      animal.sound();
              }
    
              public T getBigger(T target) {
                      return animal.getSize() > target.getSize() ? animal : target;
              }
      }
  • 컴파일 후

      public class AnimalHospitalV3 {
              private Animal animal;
    
              public void set(Animal animal) {
                      this.animal = animal;
              }
    
              public void checkup() {
                      System.out.println("동물 이름: " + animal.getName());
                      System.out.println("동물 크기: " + animal.getSize());
                      animal.sound();
              }
    
              public Animal getBigger(Animal target) {
                      return animal.getSize() > target.getSize() ? animal : target;
              }
      }
    
      ----
    
      AnimalHospitalV3 hospital = new AnimalHospitalV3();
      ...
      Dog dog = (Dog) animalHospitalV3.getBigger(new Dog());
    • 자바 컴파일러가 타입 인자로 지정한 Dog로 캐스팅하는 코드 넣어줌
  • 컴파일 후에 제네릭을 다 없애거나 상한 타입으로 변경해버린다

    • 컴파일 후에 제네릭 타입에 대한 정보 없음
반응형

'Java' 카테고리의 다른 글

[JAVA] CAS 알고리즘이란?!  (0) 2024.12.11
[JAVA] volatile ⁉  (0) 2024.12.11
[JAVA] Thread vs Process  (0) 2024.12.11
[JAVA] 객체 지향의 4대 특성 - 캡!상추다  (0) 2024.12.11
[JAVA] HashMap의 내부 구현 방식  (0) 2024.12.11