제네릭에 대해 알아보자!
이번에는 동물 병원을 만들어보자.
요구사항 : 개 병원은 개만 받을 수 있고, 고양이 병원은 고양이만 받을 수 있다.
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; } }
- 개 병원은 내부에
Dog타입을 가진다.checkup(): 개의 이름과 크기를 출력하고, 개의sound()메서드를 호출한다.bigger(): 다른 개와 크기를 비교한다.
public class CatHospital { private Cat animal; public void set(Cat animal) { this.animal = animal; } public void checkup() { System.out.println("동물 이름: " + animal.getName()); System.out.println("동물 크기: " + animal.getSize()); animal.sound(); } public Cat getBigger(Cat target) { return animal.getSize() > target.getSize() ? animal : target; } }
- 고양이 병원은 내부에
Cat타입을 가진다.checkup(): 고양이의 이름과 크기를 출력하고, 고양이의sound()메서드를 호출한다.bigger(): 다른 고양이와 크기를 비교한다. 둘 중에 큰 고양이를 반환
public class AnimalHospitalMainV0 { public static void main(String[] args) { DogHospital dogHospital = new DogHospital(); CatHospital catHospital = new CatHospital(); Dog dog = new Dog("멍멍이1", 100); Cat cat = new Cat("냐옹이1", 300); // 개 병원 dogHospital.set(dog); dogHospital.checkup(); // 고양이 병원 catHospital.set(cat); catHospital.checkup(); // 문제1: 개 병원에 고양이 전달 // dogHospital.set(cat); // 다른 타입 입력: 컴파일 오류 // 문제2: 개 타입 반환 dogHospital.set(dog); Dog biggerDog = dogHospital.bigger(new Dog("멍멍이2", 200)); System.out.println("biggerDog = " + biggerDog); } }
- 이번에 만든 코드는 처음에 제시한 요구사항을 명확히 잘 지킨다.
- 요구사항 : 개 병원은 개만 받을 수 있고, 고양이 병원은 고양이만 받을 수 있어야 한다
여기서는 개 병원과 고양이 병원을 각각 별도의 클래스로 만들었다.
각 클래스 별로 타입이 명확하기 때문에 개 병원은 개만 받을 수 있고, 고양이 병원은 고양이만 받을 수 있다. 따라서, 개 병원에 고양이를 전달하면 컴파일 오류가 발생한다. 그리고 개 병원에서bigger()로 다른 개를 비교하는 경우 더 큰 개를Dog타입으로 반환한다
문제
Dog, Cat은 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; } }
Animal타입을 받아서 처리한다.(앞에서 따로 받은 것과는 다름)checkup(),getBigger()에서 사용하는animal.getName(),animal.getSize(),animal.sound()메서드는 모두Animal타입이 제공하는 메서드이다. 따라서 문제없이 호출이 가능하다.
public class AnimalHospitalMainV1 { public static void main(String[] args) { AnimalHospitalV1 dogHospital = new AnimalHospitalV1(); AnimalHospitalV1 catHospital = new AnimalHospitalV1(); Dog dog = new Dog("멍멍이1", 100); Cat cat = new Cat("냐옹이1", 300); // 개 병원 dogHospital.set(dog); dogHospital.checkup(); // 고양이 병원 catHospital.set(cat); catHospital.checkup(); // 문제1: 개 병원에 고양이 전달 dogHospital.set(cat); // 매개변수 체크 실패: 컴파일 오류가 발생하지 않음 // 문제2: 개 타입 반환, 캐스팅 필요 dogHospital.set(dog); Dog biggerDog = (Dog) dogHospital.getBigger(new Dog("멍멍이2", 200)); System.out.println("biggerDog = " + biggerDog); } }실행 결과
동물 이름 : 멍멍이1
동물 크기 : 100
멍멍
동물 이름: 냐옹이1
동물 크기: 300
냐옹
biggerDog = Animal{name='멍멍이2', size=200}
문제
AnimalHospitalV1 하나로 개와 고양이를 모두 처리한다.Animal 타입을 반환하기 때문에 다운 캐스팅을 해야 한다.제네릭을 도입해서 코드 재사용은 늘리고, 타입 안정성 문제도 해결해보자.
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; } }
<T>를 사용해서 제네릭 타입을 선언했다.제네릭 타입을 선언하면 자바 컴파일러 입장에서
T에 어떤 값이 들어올지 예측할 수 없다.우리는
Animal타입의 자식이 들어오기를 기대했지만 코드 어디에도Animal에 대한 정보는 없다.
T에는 타입 인자로Integer가 들어올 수도 있고,Dog가 들어올 수도 있다.
물론,Object가 들어올 수도 있다.
- 자바 컴파일러는 어떤 타입이 들어올 지 알 수 없기 때문에
T를 어떤 타입이든 받을 수 있는 모든 객체의 최종 부모인Object타입으로 가정한다.- 따라서
Object가 제공하는 메서드만 호출할 수 있다.- 원하는 기능을 사용하려면
Animal타입이 제공하는 기능들이 필요한데, 이 기능을 모두 사용할 수 없다.- 여기에 추가로 한가지 문제가 더 있다. 바로 동물 병원에
Integer,Object같은 동물과 전혀 관계 없는 타입을 타입인자로 전달 할 수 있다는 점이다. 우리는 최소한Animal이나 그 자식을 타입 인자로 제한하고 싶다.
문제
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 extends Animal>이다.- 타입 매개변수
T를Animal과 그 자식만 받을 수 있도록 제한을 두는 것이다. 즉T의 상한이Animal이 되는 것이다.- 이렇게 하면 타입 인자로 들어올 수 있는 값이
Animal과 그 자식으로 제한된다.
T에 입력될 수 있는 값의 범위를 예측할 수 있다.T 에는 타입 인자로 Animal , Dog , Cat 만 들어올 수 있다. 따라서 이를 모두 수용할 수 있는 Animal 을 T 의 타입으로 가정해도 문제가 없다.Animal 이 제공하는 getName() , getSize() 같은 기능을 사용할 수 있다.public class AnimalHospitalMainV3 { public static void main(String[] args) { AnimalHospitalV3<Dog> dogHospital = new AnimalHospitalV3<>(); AnimalHospitalV3<Cat> catHospital = new AnimalHospitalV3<>(); Dog dog = new Dog("멍멍이1", 100); Cat cat = new Cat("냐옹이1", 300); //개 병원 dogHospital.set(dog); dogHospital.checkup(); //고양이 병원 catHospital.set(cat); catHospital.checkup(); // 문제1 해결: 개 병원에 고양이 전달 // dogHospital.set(cat); // 다른 타입 입력: 컴파일 오류 // 문제2 해결: 개 타입 반환 dogHospital.set(dog); Dog biggerDog = dogHospital.getBigger(new Dog("멍멍이2", 200));System.out.println("biggerDog = " + biggerDog); } }타입 매개변수에 입력될 수 있는 상한을 지정해서 문제를 해결했다
AnimalHospitalV3<Integer>와 같이 동물과 전혀 관계없는 타입 인자를 컴파일 시점에 막는다.- 제네릭 클래스 안에서
Animal의 기능을 사용할 수 있다.
정리
이번에는 특정 메서드에 제네릭을 적용하는 제네릭 메서드에 대해 알아보자.
참고로 앞서 살펴본 제네릭 타입과 지금부터 살펴볼 제네릭 메서드는 둘다 제네릭을 사용하기는 하지만 서로 다른 기능을 제공한다.
public class GenericMethod { public static Object objMethod(Object obj){ System.out.println("object print:" + obj); return obj; } public static<T> T genericMethod(T t){ System.out.println("generic print: " +t); return t; } public static <T extends Number> T numberMethod(T t){ System.out.println("bound print:" + t); return t; } }
public class MethodMain1 { public static void main(String[] args) { Integer i = 10; Object object = GenericMethod.objMethod(i); // 타입 인자(Type Argument) 명시적 전달 System.out.println("명시적 타입 인자 전달"); Integer result = GenericMethod.<Integer>genericMethod(i); Integer integerValue = GenericMethod.<Integer>numberMethod(10); Double doubleValue = GenericMethod.<Double>numberMethod(20.0); //타입 추론, 타입 인자 생략 System.out.println("타입 추론"); Integer result2 = GenericMethod.genericMethod(i); Integer integerValue2 = GenericMethod.numberMethod(10); Double doubleValue2 = GenericMethod.numberMethod(20.0); } }실행 결과
object print: 10
명시적 타입 인자 전달
generic print: 10
bound print: 10
bound print: 20.0
제네릭 타입과 제네릭 메서드의 비교
1. 제네릭 타입
- 정의 :
GenericClass<T>- 타입 인자 전달 : 객체를 생성하는 시점
- 예)
new GenericClass<String>2. 제네릭 메서드
- 정의 :
<T> T genericMethod(T t)
타입 인자 전달: 메서드를 호출하는 시점
예)GenericMethod.<Integer>genericMethod(i)
- 제네릭 메서드는 클래스 전체가 아니라 특정 메서드 단위로 제네릭을 도입할 때 사용한다.
- 제네릭 메서드를 정의할 때는 메서드의 반환 타입 왼쪽에 다이아몬드를 사용해서
<T>와 같이 타입 매개변수를 적어준다.- 제네릭 메서드는 메서드를 실제 호출하는 시점에 다이아몬드를 사용해서
<Integer>와 같이 타입을 정하고 호출한다.
제네릭 메서드의 핵심은 호출하는 시점에 타입 인자를 전달해서 타입을 지정하는 것이다. 따라서, 타입을 지정하면서 메서드를 호출한다.
인스턴스 메서드, static 메서드
제네릭 메서드는 인스턴스 메서드와 static 메서드에 모두 적용할 수 있다.
class Box<T> { //제네릭 타입 static <V> V staticMethod2(V t) {} //static 메서드에 제네릭 메서드 도입 <Z> Z instanceMethod2(Z z) {} //인스턴스 메서드에 제네릭 메서드 도입 가능 }
참고
- 제네릭 타입은 static 메서드에 타입 매개변수를 사용할 수 없다.
- 제네릭 타입은 객체를 생성하는 시점에 타입이 정해진다.
- 그런데, static 메서드는 인스턴스 단위가 아니라 클래스 단위로 작동하기 때문에 타입과는 무관하다.
- 따라서, static 메서드에 제네릭을 도입하려면 제네릭 메서드를 사용해야 한다.
class Box<T> { T instanceMethod(T t) {} //가능 static T staticMethod1(T t) {} //static 메서드에 제네릭 타입의 T 사용 불가능 }
타입 매개변수 제한
제네릭 메서드도 제네릭 타입과 마찬가지로 타입 매개변수를 제한할 수 있다.
참고로Integer,Doble,Long과 같은 숫자 타입이Number의 자식이다.public static <T extends Number> T numberMethod(T t) {}
제네릭 메서드 타입 추론
제네릭 메서드를 호출할 때
<Integer>와 같이 타입 인자를 계속 전달하는 것은 매우 불편하다.Integer i = 10; Integer result = GenericMethod.<Integer>genericMethod(i);
- 자바 컴파일러는
genericMethod()에 전달되는 인자i의 타입이Integer라는 것을 알 수 있다.- 또한 반환 타입이
Integer result라는 것도 알 수 있다. 이런 정보를 통해 자바 컴파일러는 타입 인자를 추론할 수 있다.
앞서 만든 MethodMain1 에 다음 코드를 추가해서 실행해보자
//타입 추론, 타입 인자 생략 System.out.println("타입 추론"); Integer result2 = GenericMethod.genericMethod(i); Integer integerValue2 = GenericMethod.numberMethod(10); Double doubleValue2 = GenericMethod.numberMethod(20.0);실행 결과
// 추가한 내용만 출력 타입 추론 generic print: 10 bound print: 10 bound print: 20.0
- 타임 추론 덕분에 타입 인자를 직접 전달하는 불편함이 줄어든다. 이 경우 타입을 추론해서 컴파일러가 대신 처리하기 때문에 타입을 전달하지 않는 것 처럼 보인다. 하지만, 실제로는 타입 인자가 전달되는 것이다.
앞서 제네릭 타입으로 만들었던 AnimalHospitalV3 의 주요 기능을 제네릭 메서드로 다시 만들어보자.
제네릭 메서드 활용
public class AnimalMethod { 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 getBigger(T t1, T t2) { return t1.getSize() > t2.getSize() ? t1 : t2; } }
checkup(),getBigger()라는 두 개의 제네릭 메서드를 정의했다. 둘 다Animal을 상한으로 제한한다.public class MethodMain2 { public static void main(String[] args) { Dog dog = new Dog("멍멍이", 100); Cat cat = new Cat("냐옹이", 100); AnimalMethod.checkup(dog); AnimalMethod.checkup(cat); Dog targetDog = new Dog("큰 멍멍이", 200); Dog bigger = AnimalMethod.getBigger(dog, targetDog); System.out.println("bigger = " + bigger); } }
- 기존 코드와 같이 작동하는 것을 확인할 수 있다.
- 참고로 제네릭 메서드를 호출할 때 타입 추론을 사용했다.
제네릭 타입과 제네릭 메서드의 우선순위
public class ComplexBox<T extends Animal> { private T animal; public void set(T animal) { this.animal = animal; } public <T> T printAndReturn(T t) { System.out.println("animal.className: " + animal.getClass().getName()); System.out.println("t.className: " + t.getClass().getName()); // t.getName(); // 호출 불가 메서드는 <T> 타입이다. <T extends Animal> 타입이 아니다. return t; } }public class MethodMain3 { public static void main(String[] args) { Dog dog = new Dog("멍멍이", 100); Cat cat = new Cat("냐옹이", 50); ComplexBox<Dog> hospital = new ComplexBox<>(); hospital.set(dog); Cat returnCat = hospital.printAndReturn(cat); System.out.println("returnCat = " + returnCat); } }실행 결과
animal.className: generic.animal.Dog
t.className: generic.animal.Cat
returnCat = Animal{name='냐옹이', size=50}
제네릭 타입 설정
class ComplexBox<T extends Animal>
제네릭 메서드 설정
<T> T printAndReturn(T t)
제네릭 타입보다 제네릭 메서드가 높은 우선순위를 가진다.
따라서 printAndReturn() 은 제네릭 타입과는 무관하고 제네릭 메서드가 적용된다.
여기서 적용된 제네릭 메서드의 타입 매개변수 T 는 상한이 없다. 따라서 Object 로 취급된다.
Object 로 취급되기 때문에 t.getName() 과 같은 Animal 에 존재하는 메서드는 호출할 수 없다.
이번에는 제네릭 타입을 조금 더 편리하게 사용할 수 있는 와일드카드에 대해 알아보자. 참고로 와일드카드라는 뜻은 컴퓨터 프로그래밍에서 * , ? 와 같이 하나 이상의 문자들을 상징하는 특수 문자를 뜻한다. 쉽게 이야기해서 여러 타입이 들어올 수 있다는 뜻이다.
public class Box<T>{ private T value; public void set(T value) { this.value = value; } public T get() { return value; } }
- 단순히 데이터를 넣고 반환할 수 있는 제네릭 타입을 하나 만들었다.
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; } }
- 제네릭 메서드와 와일드 카드를 비교할 수 있게 같은 기능을 각각 하나씩 배치해두었다.
- 와일드카드는
?를 사용해서 정의한다.
public class WildcardMain1 { 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)); WildcardEx.printGenericV1(dogBox); WildcardEx.printWildcardV1(dogBox); WildcardEx.printGenericV2(dogBox); WildcardEx.printWildcardV2(dogBox); Dog dog = WildcardEx.printAndReturnGeneric(dogBox); Animal animal = WildcardEx.printAndReturnWildcard(dogBox); } }참고!!!
와일드카드는 제네릭 타입이나, 제네릭 메서드를 선언하는 것이 아니다. 와일드카드는 이미 만들어진 제네릭 타입을 활
용할 때 사용한다.
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()); }
- 제네릭 메서드와 마찬가지로 와일드카드에도 상한 제한을 둘 수 있다.
- 여기서는
? extends Animal을 지정했다.Animal과 그 하위 타입만 입력 받는다. 만약 다른 타입을 입력하면 컴파일 오류가 발생한다.box.get()을 통해서 꺼낼 수 있는 타입의 최대 부모는Animal이 된다. 따라서Animal타입으로 조회할 수 있다.- 결과적으로
Animal타입의 기능을 호출할 수 있다.
타입 매개변수가 꼭 필요한 경우
Box<Dog> ,Box<Cat> 처럼 타입 인자가 전달된 제네릭 타입을 활용할 때 사용한다.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; }
printAndReturnGeneric()은 다음과 같이 전달한 타입을 명확하게 반환할 수 있다.Dog dog = WildcardEx.printAndReturnGeneric(dogBox)반면에
printAndReturnWildcard()의 경우 전달한 타입을 명확하게 반환할 수 없다. 여기서는Animal타입으로 반환한다.Animal animal = WildcardEx.printAndReturnWildcard(dogBox)
- 메서드의 타입들을 특정 시점에 변경하려면 제네릭 타입이나, 제네릭 메서드를 사용해야 한다.
- 와일드카드는 이미 만들어진 제네릭 타입을 전달 받아서 활용할 때 사용한다.
- 따라서 메서드의 타입들을 타입 인자를 통해 변경할 수 없다. 쉽게 이야기해서 일반적인 메서드에 사용한다고 생각하면 된다.
정리하면 제네릭 타입이나 제네릭 메서드가 꼭 필요한 상황이면 <T>를 사용하고, 그렇지 않은 상황이면 와일드카드를 사용하는 것을 권장한다.
와일드카드는 상한 뿐만 아니라 하한도 지정할 수 있다.
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)); } }실행 결과
animal = Animal{name='멍멍이', size=100}Box<? super Animal> box
- 이 코드는
?가Animal타입을 포함한Animal타입의 상위 타입만 입력 받을 수 있다는 뜻이다.
정리하면 다음과 같다.
Box<Object> objBox: 허용Box<Animal> animalBox: 허용Box<Dog> dogBox: 불가Box<Cat> catBox: 불가
- 하한을
Animal로 제한했기 때문에Animal타입의 하위 타입인Box<Dog>는 전달할 수 없다.
이레이저는 지우개라는 뜻이다.
- 제네릭은 자바 컴파일 단계에서만 사용되고, 컴파일 이후에는 제네릭 정보가 삭제된다.
- 제네릭에 사용한 타입 매개변수가 모두 사라지는 것이다.
- 쉽게 이야기해서 컴파일 전인
.java에는 제네릭의 타입 매개변수가 존재하지만, 컴파일 이후인 자바 바이트코드.class에는 타입 매개변수가 존재하지 않는 것이다.
어떻게 변하게 되는지 아래 코드를 확인해보자.
제네릭 타입 선언
GenericBox.javapublic class GenericBox<T>{ private T value; public void set(T value) { this.value = value; } public T get() { return value; } }
- 제네릭 타입을 선언했다.
new GenericBox<Integer>() 에 대해 다음과 같이 이해한다.public class GenericBox<Integer> { private Integer value; public void set(Integer value) { this.value = value; } public Integer get() { return value; } }
.class 에 생성된 정보는 다음과 같다.컴파일 후
GenericBox.classpublic class GenericBox { private Object value; public void set(Object value) { this.value = value; } public Object get() { return value; } }
- 상한 제한 없이 선언한 타입 매개변수
T는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; } }// 사용 코드 예시(main에서) AnimalHospitalV3<Dog> hospital = new AnimalHospitalV3<>(); ... Dog dog = animalHospitalV3.getBigger(new Dog());
컴파일 후
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; } }
T의 타입 정보가 제거되어도 상한으로 지정한Animal타입으로 대체되기 때문에Animal타입의 메서드를 사용하는데 아무런 문제가 없다.
컴파일 이후에는 제네릭의 타입 정보가 존재하지 않는다. .class 로 자바를 실행하는 런타임에는 우리가 지정한 Box<Integer> , Box<String> 의 타입 정보가 모두 제거된다.
따라서 런타임에 타입을 활용하는 코드는 작성할 수 없다.
소스 코드
class EraserBox<T> { public boolean instanceCheck(Object param) { return param instanceof T; // 오류 } public void create() { return new T(); // 오류 } }
런타임
class EraserBox { public boolean instanceCheck(Object param) { return param instanceof Object; // 오류 } public void create() { return new Object(); // 오류 } }
- 여기서
T는 런타임에 모두Object가 되어버린다.instanceof는 항상Object와 비교하게 된다. 이렇게 되면 항상 참이반환되는 문제가 발생한다. 자바는 이런 문제 때문에 타입 매개변수에instanceof를 허용하지 않는다.new T는 항상new Object가 되어버린다. 개발자가 의도한 것과는 다르다. 따라서 자바는 타입 매개변수에new를 허용하지 않는다.