Java Generic - 메소드 활용하기

기존의 동물 병원.. Animal Hospital 작성한 것이 있다.

작성한 자바 코드는 제네릭 타입으로 사용하였으니, 제네릭 메소드로 변경해보도록 한다.

AnimalHospital.class - Before

public class AnimalHospital<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;
    }
}

AnimalHospital.class - After

public class AnimalHospital {
    public static <T extends Animal> void checkup(T t) {
        System.out.println("동물의 이름 = " + t.getName());
        System.out.println("동물의 몸무게 = " + t.getSize());
        t.sound();
    }

    public static <T extends Animal> T bigger(T t1, T t2) {
        return t1.getSize() > t2.getSize() ? t1 : t2;
    }
}
  • 제네릭 메소드로 public static <T extends Animal> void checkup(T t) {...} 선언하였다.
  • set 메소드로 받아올 원본이 사라졌으므로 매개변수로 Animal의 객체들을 받아 와야한다.

Main.class

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("강아지", 15);
        Cat cat = new Cat("고양이", 8);

        AnimalHospital.checkup(dog);
        AnimalHospital.checkup(cat);

        Dog newDog = new Dog("작은 강아지", 5);

        Dog getBigDog = AnimalHospital.bigger(dog, newDog);
        System.out.println("bigger = " + getBigDog);
    }
}

동물의 이름 = 강아지
동물의 몸무게 = 15

동물의 이름 = 고양이
동물의 몸무게 = 8

bigger = Animal{name='강아지', size=15}

Dog getBigDog = AnimalHospital.<Dog>bigger(dog, newDog);

위의 코드처럼 명시적으로 제너릭을 넣지 않아도 동작하는 모습으로 타입 추론을 사용한다.


우선순위 - 제네릭 메소드, 제네릭 타입

둘 의 특징

  • 정적 메소드에는 제네릭 메소드가 사용 가능
  • 인스턴스 메소드는 제네릭 타입, 제네릭 메소드 사용 가능

Complex.class

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("강아지", 15);
        Cat cat = new Cat("고양이", 8);

        Complex complex = new Complex();

        complex.set(dog);
        complex.print(cat);
    }
}
  • 제네릭 타입 T로 선언하였다.
  • 제네릭 메소드는 Z로 메소드에 선언하였다.

다음 호출 결과를 살펴보자.

Main.java

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("강아지", 15);
        Cat cat = new Cat("고양이", 8);

        Complex complex = new Complex();

        complex.set(dog);
        complex.print(cat);
    }
}

animal.className = Dog
z.className = Cat

  • 각각 타입과 메소드를 T, Z 분리해서 표현하였다.

제네릭 타입 설정

class ComplexBox<T extends Animal>

제네릭 메소드 설정

<Z> Z print(Z z)

다음으로 메소드 타입 모두 T 이름으로 통일시킨 경우 어떻게 되는지 살펴보자.

Complex.class - 제너릭 이름 통일

public class Complex<T extends Animal> {
    private T animal;

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

    public <T> T print(T t) {
        System.out.println("animal.className = " + animal.getClass().getName());
        System.out.println("t.className = " + t.getClass().getName());
        return t;
    }
}

Main.java

public class Main {
    public static void main(String[] args) {
        Dog dog = new Dog("강아지", 15);
        Cat cat = new Cat("고양이", 8);

        Complex complex = new Complex();

        complex.set(dog);
        complex.print(cat);
    }
}

animal.className = Dog
t.className = Cat

이름 충돌로 덮어쓰거나 발생되지 않고 정상적으로 출력한다.

print() 제네릭 타입이 무관한 제네릭 메소드가 출력하고 있다.
결과로 제네릭 타입보다는 제너릭 메소드이 우선순위가 높다고 볼 수 있다.

번외로 제너릭 메소드에 적용되는 타입 매개변수 T 에는 파라미터 상한이 없다. Object로만 취급하므로 Animal의 getName() 이름을 호출할 수 없는 것을 살펴볼 수 있다.