인프런 커뮤니티 질문&답변
제네릭 타입 매개변수 제한과 관련한 문의입니다.
작성
·
29
0
[질문 템플릿]
1. 강의 내용과 관련된 질문인가요? 예
2. 인프런의 질문 게시판과 자주 하는 질문에 없는 내용인가요? 예
3. 질문 잘하기 메뉴얼을 읽어보셨나요? 예
[질문 내용]
영한님, 안녕하세요.
강의를 들으며 부족하다고 느껴졌던 기본기를 덕분에 잘 습득하고 있습니다!
타입 매개변수 제한 강의를 듣고 나서 궁금한 점이 생겨 Q&A를 남기게 되었습니다.
제네릭을 도입하고 타입 매개변수를 제한함으로써,
1. 코드의 재사용성 증가
2. 타입 안정성 보장
위 두 가지의 강력한 장점을 가진다는 부분을 잘 이해했습니다.
다만, 클라이언트 코드에서 인스턴스를 생성하는 과정에 타입 매개변수를 아래와 같이 Animal로 지정하게 된다면 개발자의 실수가 발생할 수 있는 부분이 있다고 생각합니다.
AnimalHospitalV3<Animal> animalHospital = new AnimalHospitalV3<>();
animalHospital.set(new Dog("dog", 100));
Dog biggerAnimal = (Dog) animalHospital.bigger(new Cat("cat", 100));위와 같은 코드는 다형성만을 사용하여 개션한 코드와 같이 Java 컴파일러가 잡아주지 못하고, 런타임 시점에 오류가 발생하게 됩니다. (Animal 클래스를 추상 클래스, interface로 선언해도 타입 인자로 지정하는 것을 막지 못합니다.)
이런 상황일 때, 개발자의 실수를 최대한 방지할 수 있는 방법이 어떤 것이 있을지 궁금합니다.
항상 좋은 강의 만들어주셔서 감사합니다.
덕분에 잘 배우며 기본기를 다져가고 있습니다!
답변 3
0
안녕하세요. 서규환님
AnimalHospitalV3<Animal>처럼 상위 타입을 타입 인자로 직접 지정하면, Dog과 Cat이 섞여 들어와도 컴파일러가 잡아주지 못하는 것이 맞습니다.
결론부터 말씀드리면, 이것은 제네릭의 한계가 아니라 사용하는 쪽의 책임입니다.
제네릭의 설계 의도를 먼저 생각해보면
제네릭은 "사용하는 시점에 타입을 구체적으로 결정하라"는 것이 핵심입니다. 즉, 다음과 같이 사용하는 것이 올바른 사용법입니다.
AnimalHospitalV3<Dog> dogHospital = new AnimalHospitalV3<>();
AnimalHospitalV3<Cat> catHospital = new AnimalHospitalV3<>();AnimalHospitalV3<Animal>로 선언하는 것은, 개발자가 스스로 "나는 모든 Animal을 다 받겠다"고 선언한 것입니다. 이것은 제네릭을 쓰면서 일부러 다형성 방식으로 돌아간 것이기 때문에, 그에 따르는 책임(캐스팅 등)도 개발자가 지게 됩니다.
그러면 실무에서 실수를 줄이는 방법은 무엇이 있을까요?
1. 코드 리뷰와 컨벤션으로 방지
실무에서 가장 현실적인 방법입니다. "타입 인자로 상위 타입을 직접 지정하지 말 것"이라는 팀 컨벤션을 정하는 것입니다. 사실 대부분의 경우 <Animal>로 지정할 이유가 없습니다. 구체적인 타입으로 지정하는 것이 제네릭의 올바른 활용법이니까요.
2. 와일드카드를 활용한 읽기 전용 제한
만약 여러 Animal 타입을 함께 다뤄야 하는 상황이라면, 와일드카드를 활용할 수 있습니다. 이 부분은 뒤에서 학습하게 됩니다.
// 데이터를 꺼내서 읽기만 하는 경우
void printAnimal(AnimalHospitalV3<? extends Animal> hospital) {
// hospital.set(new Dog(...)); // 컴파일 오류! 넣을 수 없음
Animal animal = hospital.get(); // 꺼내는 것만 가능
}이렇게 하면 꺼내서 읽는 것은 가능하지만, 잘못된 타입을 넣는 실수는 컴파일 시점에 막을 수 있습니다.
3. 근본적인 관점
자바의 타입 시스템에서 AnimalHospitalV3<Animal>이라는 선언 자체를 금지하는 방법은 없습니다. 질문에서 언급하신 것처럼 Animal을 추상 클래스나 인터페이스로 만들어도, 타입 인자로 지정하는 것까지 막지는 못합니다.
하지만 이것은 Object obj = new Dog()와 본질적으로 같은 문제입니다. 자바에서 상위 타입으로 참조하는 것 자체를 금지할 수는 없고, 그렇게 하는 순간 발생하는 책임은 개발자에게 있는 것입니다.
정리하면
제네릭의 가치는 <Dog>, <Cat>처럼 구체적인 타입을 지정했을 때 빛을 발합니다. <Animal>로 지정하는 것은 제네릭의 장점을 스스로 포기하는 것이므로, "구체적인 타입을 지정해서 사용하는 것이 원칙"이라고 이해하시면 됩니다. 실무에서는 코드 리뷰를 통해 이런 부분을 잡아내고, 필요한 경우 와일드카드를 활용하는 것이 가장 현실적인 방법입니다.
하지만 약간의 대안도 있습니다.
1. 구체적인 타입 인자 사용 원칙 (가장 기본적이고 권장되는 방법)
제네릭의 본래 도입 목적은 사용할 타입을 명확히 지정하여 컴파일 시점에 오류를 잡는 것입니다. 따라서 객체를 생성할 때 AnimalHospitalV3<Animal> 대신, AnimalHospitalV3<Dog>, AnimalHospitalV3<Cat>과 같이 명확한 하위 타입을 지정해서 생성하는 것을 기본 원칙으로 삼아야 합니다. 이렇게 구체 타입을 명시하면, 개 병원에 고양이를 넣으려고 할 때 컴파일러가 완벽하게 오류를 잡아냅니다.
2. 정적 팩토리 메서드를 활용한 객체 생성 제한
클라이언트 개발자가 실수로 <Animal>을 넣어서 인스턴스를 생성하는 것을 아예 막고 싶다면, 생성자를 막고 정적 팩토리 메서드를 제공하는 방식을 사용할 수 있습니다.
public class AnimalHospitalV3<T extends Animal> {
// 1. 외부에서 직접 생성을 막음
private AnimalHospitalV3() {}
// 2. 정적 팩토리 메서드로 특정 구체 타입만 생성하도록 유도
public static AnimalHospitalV3<Dog> createDogHospital() {
return new AnimalHospitalV3<>();
}
public static AnimalHospitalV3<Cat> createCatHospital() {
return new AnimalHospitalV3<>();
}
// ...
}
이렇게 설계하면 개발자가 무심코 new AnimalHospitalV3<Animal>()을 호출하는 것을 구조적으로 차단하고, 허용된 구체 타입의 병원만 생성하도록 강제할 수 있습니다.
감사합니다.
0
0
안녕하세요, 인프런 AI 인턴입니다. 서규환님!
타입 매개변수 제한을 사용해도 런타임 오류를 완전히 피할 수 없다는 점에 대한 질문을 주셨네요. 제네릭을 사용할 때 여전히 개발자의 실수로 인해 런타임 오류가 발생할 가능성을 최소화하는 방법을 생각해보겠습니다.
타입 안전성을 높이는 방법
타입 제한을 강화하기: 제네릭 타입 매개변수에 더 구체적인 제한을 추가하여 사용자가 특정 타입 이상을 허용하지 않도록 설정할 수 있습니다. 이를 통해 잘못된 타입 사용을 막을 수 있습니다.
불변 객체 사용: 컬렉션에 넣는 객체를 불변 객체로 만들면 불변성을 보장하면서 사용자의 실수를 줄일 수 있습니다. 불변 객체는 객체 상태를 변경하지 않으므로, 타입 에러가 발생할 가능성을 줄입니다.
컴파일 타임 체크 강화: Java에서 제공하는 다양한 체크(예:
@SafeVarargs,@UncheckedWarnings제거 등)를 통해 컴파일 타임에 최대한 많은 오류를 잡도록 설정할 수 있습니다.클래스 확장 금지: 상속을 통한 구현을 제한하여, 불필요한 타입 사용을 막을 수 있습니다. 예를 들어,
final키워드를 사용하면 클라이언트 코드에서의 불필요한 상속을 막을 수 있습니다.디자인 패턴 사용: 전략 패턴이나 팩토리 패턴을 사용하여 개체 생성의 책임을 관리하는 것도 하나의 방법입니다.
유사한 사례 및 해결책을 제공하는 링크
아래의 링크들도 참고하면 도움이 되실 것입니다.
타입 매개변수 제한 강의 질문: 제네릭에서 타입 매개변수 제한의 오류와 해결 방법 논의.
타입 매개변수 추론 질문 드립니다.: 제네릭 타입 추론에 대한 이해를 돕는 질문과 답변.
이런 방법들이 런타임 오류를 줄이며 코드 품질을 높이는 데 도움이 되시길 바랍니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.





