프로그래밍백엔드 개발자

자바에서 제너릭(generics)의 생성 및 사용의 주요 측면에 대해 설명해 주십시오. 안전한 적용을 위해 알아야 할 중요한 세부 사항은 무엇입니까?

Hintsage AI 어시스턴트로 면접 통과

답변.

제너릭은 타입 매개변수를 사용하여 클래스, 인터페이스 및 메서드를 생성할 수 있도록 하여 컴파일 타임에 타입 검사를 수행하고 ClassCastException을 피할 수 있도록 도와줍니다.

주요 특징 및 함정:

  • 타입 소거(Type Erasure): 컴파일 타임에 타입 매개변수에 대한 정보가 지워지므로, 예를 들어 제너릭 타입의 배열을 생성할 수 없습니다: new List<String>[10] — 컴파일 오류가 발생합니다.
  • 사용 제한:
    • 매개변수화된 타입의 인스턴스를 생성할 수 없습니다: T obj = new T();
    • 매개변수화된 타입에 대해 instanceof를 사용할 수 없습니다: if(obj instanceof List<String>) — 오류가 발생합니다.
    • 매개변수화된 타입의 정적 필드를 생성할 수 없습니다.
  • 와일드카드(Wildcards): ? extends T — 공변성(읽기), ? super T — 반공변성(쓰기).
  • PECS 원칙(Producer Extends, Consumer Super): 읽기만 필요하면 extends를 사용하고, 쓰기만 필요하면 super를 사용하십시오.

예:

// 읽기를 위한 공변적 접근 void printNumbers(List<? extends Number> numbers) { for (Number n : numbers) { System.out.println(n); } } // 쓰기를 위한 반공변적 접근 void addIntegers(List<? super Integer> list) { list.add(10); } }

함정이 있는 질문.

질문: "List<Object>와 List<?>의 차이점은 무엇입니까? List<?>에 어떤 객체라도 추가할 수 있습니까?"

답변: 아니요, List<?>에는 아무것도 추가할 수 없습니다( null을 제외하고), 컴파일러가 어떤 타입 매개변수가 있는지 모르기 때문입니다. 반면 List<Object>에는 어떤 객체든 추가할 수 있습니다.

예:

List<?> list1 = new ArrayList<String>(); // list1.add("test"); // 컴파일 오류! List<Object> list2 = new ArrayList<>(); list2.add("test"); // OK

주제에 대한 세부 사항을 모르면 발생하는 실제 오류 사례들.


이야기

개발 팀은 매개변수화된 타입 T[]을 기반으로 캐시를 구현하려고 했습니다. 타입 소거(type erasure)와 제너릭 타입의 배열을 생성할 수 없는 이유로 예상대로 작동하지 않았습니다: Object[] 배열이 생성되어 runtime 캐스팅 시 ClassCastException이 발생했습니다.


이야기

하나의 마이크로서비스에서 개발자는 List<?>를 매개변수로 사용하는 수신기를 구현하려고 했으며 컬렉션을 수정하려고 했습니다. 이는 컴파일 오류를 발생시켰고, PECS를 감안하여 로직을 리팩토링해야 하여 릴리스가 지연되었습니다.


이야기

외부 시스템과의 통합 프로젝트에서 개발자는 처리되지 않은 raw-type으로 List를 통해 한 타입의 컬렉션을 다른 타입으로 덮어쓰는 실수를 저질렀습니다: List list = new ArrayList<String>(); 이로 인해 ClassCastException이 발생하고, 서비스가 다른 타입으로 요소를 캐스팅하려고 할 때 production에서 장애가 발생했습니다.