앞서 살펴본 Generic과 다형성에서 보았던 답답함을 해결하는 간단한 방법은 와일드 카드를 사용하는 것입니다. feedAnimals메소드를 다음과 같이 수정합니다.

private void feedAnimals(List<? extends Animal> animals) {

             for(Animal animal : animals){

                    animal.eat();

             }

       }

위 코드는 아래와 같이 써도 똑같은 의미 입니다. 다른 의미 입니다. 위에 것은 상위 제한이 Animal인 어떠한 타입이든(Dog든 Cat이든 Animal을 상속 받았다면..) 올 수 있는데 아래 것은 상위 타입이 Animal인 어떤 타입 하나(Dog면 Dog, Cat이면 Cat)만 올 수 있기 때문에 콜렉션에 타입 제약을 가할 수 있기 때문에 추가가 됩니다. T 타입으로 추가를 해야겠죠.

private <T extends Animal> void feedAnimals(List<T> animals) {

             for(Animal animal : animals){

                    animal.eat();

             }

       }

그럼 이런 상황에서 Dog 리스트에 Cat 객체를 넣으려는 건 어떻게 방지가 될까가 궁금합니다. 컴파일러는 위와 같이 ? 나 T 같은 와일드 카드[footnote]Java 에서 identifier가 될 수 있는 어떤 문자도 상관이 없습니다.[/footnote]가 사용되면 컴파일러는 콜렉션에 추가하는 작업을 허용하지 않습니다.

따라서 다음고 같은 코드를 컴파일 에러를 발생시킵니다.

private <T extends Animal> void feedAnimals(List<T> animals) {

             for(Animal animal : animals){

                    animal.eat();

             }

             animals.add(new Dog());   //에러

             animals.add(new Cat());   //에러

             animals.add(new Animal());  //에러

       }

하지만 다음과 같이 추가하는 작업을 할 수 있습니다.

    private <T extends Animal> void feedAnimals(List<T> animals, T a) {
        for(Animal animal : animals){
            animal.eat();
        }
//        animals.add(new Dog());
//        animals.add(new Cat());
//        animals.add(new Animal());
        animals.add(a);
    }

이게 되는 이유는 T라는 타입 하나로 Collection안에 들어가는 요소들의 타입이 제한 할 수 있기 때문입니다.

또 하나 여기서 사용된 extends의 의미는 class head부분에서 사용되는 extends보다 포괄적인 의미를 나타냅니다. extends와 implements를 포함합니다.

bl134.zip
참조 : Head First Java

좀더 자세한 내용은 Generics를 참조 하세요.