HomeAboutMeBlogGuest
© 2025 Sejin Cha. All rights reserved.
Built with Next.js, deployed on Vercel
✍🏻
Learnary (learn - diary)
/
🌧️
제네릭
🌧️

제네릭

progress
Done
Tags
Java
매개변수화 타입은 무공변 ‼️Generic 이란?종류
🪞
https://medium.com/@joongwon/java-java의-generics-604b562530b3

매개변수화 타입은 무공변 ‼️

Generic 이란?

  • 하나 이상의 타입 매개변수를 선언하고 있는 클래스나 인터페이스를 제네릭 클래스, 또는 제네릭 인터페이스 라고 하며 이를 제네릭 타입 이라 한다.
 
🧑🏻‍💻
사전 지식 🙏 무공변 : 오로지 자기 타입만 허용. 공변 : 구체적인 방향으로 타입 변환을 허용하는 것 (자기 자신과 자식 객체만 허용 → < ? extends T> 반공변 : 추상적인 방향으로의 타입 변환을 허용하는 것 (자기 자신과 부모 객체만 허용)
 

종류

  • 비 구체화 타입 런타입 비 구체화
    • 타입 소거자에 의해 컴파일 타임에 타입 정보가 사라지는 것.
  • 구체화 타입 런타임 구체화
    • 자신의 타입 정보를 런타임 시에 알고 지키게 하는 것
 
// before [컴파일 전] public static void main(String... args) { List<String> list = new ArrayList<>(); list.add("Hi"); Object[] array = new Long[10]; array[0] = 1L; } // after [컴파일 후] public static void main(String... var0) { ArrayList var1 = new ArrayList(); var1.add("Hi"); Long[] var2 = new Long[10]; var2[0] = Long.valueOf(1L); }
 
  • 언바운드 와일드 카드 타입
    • ex) List<?>
    • 컴파일 된 코드에서도 마찬가지로 타입에 의존하는 코드는 없으며 소거자에 의해 <?>가 지워지지 않는다.
    •  
  • 바운드 타입 매개변수
    • 특정 타입으로 제한한다는 의미이다.
    • ex ) 특정 타입의 서브 타입으로 제한시킨다고 이해하면 됨.
// Number로 타입 제한 public class Box<T extends Number> { public void set(T value) {} } public static void main(String... args) { Box<Integer> box = new Box<>(); box.set("Hi"); // compile error }
 
➕ 다중 바운드 타입
  • 클래스 하나와 인터페이스 여러 개 선언
  • 제약조건
    • 클래스는 한개만 허용되고 인터페이스는 여러개 가능
class A {} interface B {} interface C {} class D<T extends A & B & C> { }
 
  • 재귀적 타입 바운드
    • 타입 매개변수가 자신을 포함하는 수식에 의해 한정될 수 있는 재너릭
    • 대표적 Comparable
    • public interface Comparable<T> { int compareTo(T o); }
      ❓ 존재하는 이유?
    • 연산자 오버 로딩을 지원하지 않기 때문에 원시 타입에만 비교 연산자를 사용할 수 있기 때문이다.
    • 해당 문제를 해결하기 위해 Comparable 인터페이스와 재귀적 타입 바운드를 활용한다.
    •  
      public static <T> int countGreaterThan(T[] anArray, T elem) { int count = 0; for (T e : anArray) if (e > elem) // compiler error ++count; return count; } public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) { int count = 0; for (T e : anArray) if (e.compareTo(elem) > 0) ++count; return count; }
       
      연산자 오버로딩을 지원했다면 필요없을 것 같다.
      🧑🏻‍💻
      나의생각 - 연산자 오버로딩을 지원했다면 필요없을 것 같다.
       
    • 바운드 와일드 카드 타입
      • <? extends T> → Upper Bound
      • <? super T> → Lowwer Bound
      •  
        무공변 : 오로지 자기 타입만 허용하는 것 <T>
        공변 : 구체적인 방향으로 타입 변환을 허용하는 것 (자기 자신과 자식 객체만 허용) <? extends T>
        반공변 : 추상적인 방향으로의 타입 변환을 허용하는 것(자기 자신과 부모 객체만 허용) <? super T>
         
      • 제너릭은 매개변수화 타입이다 ‼️
      • List<String> strs = new ArrayList<>(); List<Object> objs = strs; String s = strs.get(0); // ClassCastException
        → RuntimeException 발생
         
        interface Collection<E> { void addAll(Collection<E> items); } void copyAll(Collection<Object> to, Collection<String> from) { to.addAll(from); // Compile Error }
        논리적으로 문제가 없어 보이지만 Collection<String> 은 Collection<Object>의 서브 타입이 아니기 때문에 컴파일에 실패한다. 위 코드가 컴파일 되려면 addAll 메소드의 시그니처를 다음과 같이 변경해야함.
         
        public interface Collection<E> { void addAll(Collection<? extends E> items); }
         
        extends-bound, super-bound PECS 라는 개념으로도 사용된다.
         
      예제 코드에서 컴파일이 되는 경우와 컴파일 되지 않는 경우를 같이 표시하였다. 위 코드를 보고 알 수 있는 것은 Producer-extends는 읽기만 가능하고 Consumer-super는 쓰기만 가능하다는 것이다.
       
      notion image
       
      🧑🏻‍💻
      Producer-extends는 읽기만 가능하고 Consumer-super는 쓰기 만 가능하다는 것이다.
       
      여기서 src는 데이터를 복사할 데이터를 제공하므로(생산) In 인자가 되고 dest는 다른 곳에서 사용할 데이터를 받아들이므로(소비) Out 인자가 되므로 In의 경우 super 키워드를 사용하고 Out의 경우는 extends를 사용하라고 한다.
       
       
      Q. 생산하다, 소비하다 개념이 모호함..