study/Java

Generic2

으녕오리 2025. 4. 2. 08:32

1. 타입 매개변수 제한

 

1.1 제네릭을 사용하지 않는 경우

  • 장점 : 타입 안전성 X
  • 단점 : 코드의 중복 O
package generic.ex3;
import generic.animal.Dog;
public class DogHospital {
    private Dog animal;
    public void set(Dog animal) {
        this.animal = animal;
    }
    public void checkup() {
        System.out.println("동물 이름: " + animal.getName());
        System.out.println("동물 크기: " + animal.getSize());
        animal.sound();
    }
    public Dog bigger(Dog target) {
        return animal.getSize() > target.getSize() ? animal : target;
    }
}

 

1.2 다형성 시도

  • 다형성을 사용하여 코드의 중복 제거 시도
  • 장점 : 코드 재사용 O
  • 단점 : 타입 안전성 x
package generic.ex3;
import generic.animal.Animal;
public class AnimalHospitalV1 {
    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;
    }
}
  • 문제1 : 개 병원에 고양이 전달
dogHospital.set(cat);
  • 문제2 : 다운 캐스팅 필요
dogHospital.set(dog);
Dog biggerDog = (Dog) dogHospital.getBigger(new Dog("멍멍이2", 200));

 

1.3 제네릭 도입과 실패

  • 문제1 : 타입 인자로 어떤 타입이든 들어올 수 있다.
  • 문제2 : 문제1로 인해 타입 매개변수를 Object로 가정하므로, Object의 기능만 사용할 수 있다.

∴ 타입 인자를 제한할 필요성이 생김

package generic.ex3;
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 null;
//return animal.getSize() > target.getSize() ? animal : target;
    }
}

 

1.4 타입 매개변수 제한

  • 타입 안전성 문제
    • 개 병원에 고양이 전달 -> 해결
    • 다운 캐스팅 필요 -> 해결
  • 제네릭 도입 문제
    • 타입 인자로 어떤 타입이든 들어올 수 있다. -> 해결
    • Object의 기능만 사용할 수 있다. -> 해결
package generic.ex3;
import generic.animal.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;
    }
}

 

2. 제네릭 메서드

  • 메서드를 호출하는 시점에 타입 인자를 전달해서 타입을 지정한다.
  • 제네릭 타입
    • 정의: GenericClass<T>
    • 타입 인자 전달: 객체를 생성하는 시점
  • 제네릭 메서드
    • 정의: <T> T genericMethod(T t)
    • 타입 인자 전달: 메서드를 호출하는 시점
  • static 메서드에 타입 매개변수 사용 불가능
class Box<T> {
    T instanceMethod(T t) {} //가능
    static T staticMethod1(T t) {} //제네릭 타입의 T 사용 불가능
}
  • 타입 매개변수 제한 가능
  • 타입 추론 가능
  • 제네릭 메서드가 제네릭 타입보다 우선순위가 높다. -> 모호하게 쓰지 말 것

 

3. 와일드카드

  • 제네릭 타입을 조금 더 편리하게 사용할 수 있다.
  • ?를 사용하여 정의한다. -> 여러 타입이 들어올 수 있다.
  • 와일드카드는 이미 만들어진 제네릭 타입을 활용할 때 사용한다.
  • 제네릭 메서드의 사용 과정은 매우 복잡하지만,
    와일드카드는 매개변수로 제네릭 타입을 받을 수 있는 것이므로 단순하다.
package generic.ex5;
import generic.animal.Animal;
public class WildcardEx {
    static <T> void printGenericV1(Box<T> box) {
        System.out.println("T = " + box.get());
    }
    static void printWildcardV1(Box<?> box) {
        System.out.println("? = " + box.get());
    }
    static <T extends Animal> void printGenericV2(Box<T> box) {
        T t = box.get();
        System.out.println("이름 = " + t.getName());
    }
    static void printWildcardV2(Box<? extends Animal> box) {
        Animal animal = box.get();
        System.out.println("이름 = " + animal.getName());
    }
    static <T extends Animal> T printAndReturnGeneric(Box<T> box) {
        T t = box.get();
        System.out.println("이름 = " + t.getName());
        return t;
    }
    static Animal printAndReturnWildcard(Box<? extends Animal> box) {
        Animal animal = box.get();
        System.out.println("이름 = " + animal.getName());
        return animal;
    }
}
  • 상한 제한과 하한 제한을 둘 수 있다.
static <T extends Animal> void printGenericV2(Box<T> box) {
    T t = box.get();
    System.out.println("이름 = " + t.getName());
}
static void printWildcardV2(Box<? extends Animal> box) {
    Animal animal = box.get();
    System.out.println("이름 = " + animal.getName());
}
package generic.ex5;
import generic.animal.Animal;
import generic.animal.Cat;
import generic.animal.Dog;
public class WildcardMain2 {
    public static void main(String[] args) {
        Box<Object> objBox = new Box<>();
        Box<Animal> animalBox = new Box<>();
        Box<Dog> dogBox = new Box<>();
        Box<Cat> catBox = new Box<>();
// Animal 포함 상위 타입 전달 가능
        writeBox(objBox);
        writeBox(animalBox);
//writeBox(dogBox); // 하한이 Animal
//writeBox(catBox); // 하한이 Animal
        Animal animal = animalBox.get();
        System.out.println("animal = " + animal);
    }
    static void writeBox(Box<? super Animal> box) {
        box.set(new Dog("멍멍이"
                , 100));
    }
}

 

4. 타입 이레이저

  • 제네릭은 자바 컴파일 단계에서만 사용되고, 컴파일 이후에는 제네릭 정보가 삭제된다.
  • 타입 매개변수에 instanceof, new 허용 x
class EraserBox<T> {
    public boolean instanceCheck(Object param) {
        return param instanceof T; // 오류
    }
    public T create() {
        return new T(); // 오류
    }
}

'study > Java' 카테고리의 다른 글

LinkedList  (0) 2025.04.04
ArrayList  (0) 2025.04.03
Generic  (0) 2025.04.01
Java에서 return문 누락으로 발생하는 오류와 해결 방법  (0) 2025.03.05
for문  (0) 2025.02.20