온라인 강의/김영한의 실전 자바 - 중급 2편

[섹션1] 제네릭 2편

코드몬스터 2024. 8. 4. 13:10
728x90

1. 타입 매개변수 제한

  •  클래스에 명확한 타입을 정해 놓고 작성한다.
    • 코드 재사용 X
    • 코드 안정성 O
  • 다형성 시도
    • 코드 재사용성 O: 다형성 처리를 통해 클래스를 하나로 처리할 수 있다.
    • 코드 안전성 X: 원하는 타입을 반환하기 위해서는 다운 캐스팅을 해야한다.
  • 제네릭 도입과 실패
    • 제네릭 타입을 선언하면 자바 컴파일러 입장에서는 T에 어떤 타입이 들어오는지 알 수 없다.
      => T는 어떤 타입이든 받을 수 있는 모든 객체의 최종 부모인 Object 타입으로 가정한다.
    • 제네릭에서 타입 매개변수를 사용하면 어떤 타입이든 들어올 수 있다.
      => 매개변수 타입을 제한 해야한다.
  • 타입 매개변수 제한
    • public class AnimalHospital<T extends Animal>
    • 코드 재사용성 O
    • 코드 안전성 O: 타입 매개변수 상한(extends)을 사용

 

2. 제네릭 메서드

1) 제네릭 타입

  • 정의: GenericClass<T>
  • 타입 인자 전달: 객체를 생성하는 시점
    • new GenericClass<String>

2) 제네릭 메서드

  • 정의: <T> T genericMethod(T t)
  • 타입 인자 전달: 메서드를 호출하는 시점
    • GenericMethod.<Integer>genericMethod(i)

3) 인스턴스 메서드, static 메서드

  • 제네릭 메서드는 인스턴스 메서드와 static 메서드에 모두 적용할 수 있다.
class Box<T> {	// 제네릭 타입
    static <V> V staticMethod2(V t) {}  // static 메서드에 제네릭 메서드 도입
    <Z> Z instanceMethod2 (Z z) {} // 인스턴스 메서드에 제네릭 메서드 도입
}

 

참고

  • 제네릭 타입은 static 메서드에 타입 매개변수를 사용할 수 없다.
class Box<T> {
    T instanceMethod(T t) {}
    static T staticMethod1(T t) {} // 제네릭 타입의 T 사용 불가능
}

 

4) 타입 매개변수 제한

  • 제네릭 메서드도 제네릭 타입과 같이 타입 매개변수를 제한할 수 있다.
  • Integer, Double, Long과 같은 숫자 타입이 Number의 자식이다.
public static <T extends Number> T numberMethod(T t) {}

 

5) 제네릭 메서드 타입 추론

  • < Integer>와 같이 타입 인자를 계속 전달하는 것은 불편하다.
  • GenericMethod.<Integer>genericMethod(i) => GenericMethod.genericMethod(i)
Integer result2 = GenericMethod.genericMethod(i);

 

제네릭 메서드 VS 제네릭 타입

  • 인스턴스 변수 animal에 Dog 클래스를 넣고, 인스턴스 메서드 printGeneric의 인자로 Cat 클래스를 넣었다.
    => 제네릭 변수 T는 어떤 클래스를 나타낼까?
    => 제네릭 타입과 메서드 중 어떤 클래스를 가질까?
  • T와 가장 가까운 쪽의 클래스를 찾는다.
    => 제네릭 메서드가 우선순위를 가진다.
  • 모호하게  T로 전부 표현하는게 아니라 제네릭 메서드에서 Z 등 다른 이름으로 변환한다.
public class Generic<T extends Animal> {

    public T animal;

    public <T> T printGeneric<T t> {
        System.out.println(t.getClassName().getName());
    }
    
}

public class Main {

    public static void main(String[] args) {

        Dog dog = new Dog("멍멍이", 100);
        Cat cat = new Cat("멍멍이", 100);

        Generic<Dog> test = new Generic();
        test.set(dog);

        Cat returnCat = test.printGeneric(cat);
    }

}

 

 

3. 와일드 카드

  • 와일드카드는 프로그래밍에서 *, ? 와 같이 하나 이상의 문자들을 상징하는 특수 문자를 뜻한다.
  • 와일드카드는 이미 만들어진 제네릭 타입을 활용할 때 사용한다. 
    => Box<Dog>, Box<Cat> 처럼 타입 인자가 정해진 제네릭 타입을 전달 받아서 활용할 때 사용한다.
    => Box<Object>도 입력될 수 있다.
// 제네릭 메서드
// 타입 추론에 의해 T는 Dog가 된다.
static <T> void printWildCardV1(Box<T> box) {
    System.out.println(box.get());
}

// 제네릭 메서트가 아니다. 일반적인 메서드
// 와일드 카드 ?는 모든 타입을 받는다.
static void printWildCardV1(Box<?> box) {
    System.out.println(box.get());
}

 

1) 상한 와일드카드

  • 와일드 카드에도 상한 제한을 둘 수 있다.
  • ? extends Animal
static <T extends Animal> void printGeneric(Box<T> box) {
    T t = box.get();
    System.out.println(t.getName());
}

public void printWildCard(Box<? extends Animal> box) {
    Animal animal = box.get();
    System.out.println(animal.getName());
}

 

2) 하한 와일드카드

  • 와일드 카드에는 하한 제한을 둘 수 있다.
    (제네릭은 불가능하다)
  • ? super Animal
  • 인자가 최소한 Animal 보다는 상위 클래스여야 한다.
  • Dog, Cat 은 불가능하고 Animla, Object는 가능하다.
static void printWildCard(Box<? super Animal> box) {
	
}

 

3) 타입 매개변수가 꼭 필요한 경우

 

 

4. 타입 이레이저

 

 

정리

  • 실무에서는 제니릭을 사용해서 설계하는 일은 드물다.
  • 이미 제네릭을 통해 만들어진 프레임워크나 라이브러리들을 가져다 사용하는 경우가 많다.
  • 공부한 내용보다 더 어려운 개념들도 있다.
    => 공변(covariant), 반공변(contravariant) 등
  • 실무에서 경험을 쌓고 필요하다고 느껴질 때 공부를 해보자..!!
  • 제네릭은 컬렉션 프레임워크에서 많이 사용된다.