제네릭이란 무엇인가?
Source: Dev.to
Generics는 Java 5에서 도입된 기능으로, 다양한 데이터 타입을 다루는 클래스, 인터페이스 및 메서드를 만들 수 있게 해줍니다. 명시적인 형변환(cast)이 필요 없게 해주며, 컴파일 시점에 오류를 감지하는 데 도움을 줍니다.
Java 5 이전에 컬렉션은 모든 요소를 Object로 취급했습니다. 이 때문에 두 가지 문제가 발생했습니다:
- 과도한 캐스팅: 리스트에서 값을 꺼낼 때마다
(String)과 같은 캐스트를 해야 했습니다. - 안전성 부족:
String만 들어가야 할 리스트에Integer가 삽입되는 것을 방지할 방법이 없어, 실행 시 오류가 발생했습니다.
// Código antigo
List lista = new ArrayList(); // Lista "crua" (Raw Type)
lista.add("Olá");
lista.add(10); // O compilador deixa passar!
// O erro só acontece aqui, quando o programa está rodando:
String texto = (String) lista.get(1); // ClassCastException!
// Código novo com Generics
List lista = new ArrayList<>();
lista.add("Olá");
// lista.add(10); // O compilador nem deixa compilar. Erro na hora!
String texto = lista.get(0); // Sem necessidade de cast manual.
타입 변수
Generics는 클래스와 메서드가 여러 타입의 객체에서 동작하도록 하며, 컴파일 시점의 안전성을 보장합니다 (compile‑time safety). 관례상, 이러한 제네릭 타입을 나타내기 위해 대문자를 사용합니다:
T: Type (제네릭 타입)E: Element (컬렉션에서 사용)K,V: Key, Value (맵에서 사용)
제네릭 클래스
제네릭 클래스는 T 로 표시되는 타입 매개변수를 사용하며, 이는 실제 타입을 위한 자리표시자 역할을 합니다.
// define que esta classe aceita um parâmetro de tipo
public class Caixa<T> {
private T conteudo;
public void guardar(T conteudo) {
this.conteudo = conteudo;
}
public T abrir() {
return conteudo;
}
}
public class AulaGenerics {
public static void main(String[] args) {
// Caixa que só aceita Strings
Caixa<String> caixaDeTexto = new Caixa<>();
caixaDeTexto.guardar("Segredo");
// caixaDeTexto.guardar(123); // Erro de compilação!
// Caixa que só aceita Inteiros
Caixa<Integer> caixaDeNumeros = new Caixa<>();
caixaDeNumeros.guardar(100);
}
}
T는 객체를 생성할 때 String, Integer 또는 다른 타입으로 대체됩니다.
Wildcards
wildcards (?)는 제네릭에서 알 수 없는 타입을 나타내며, 다양한 컬렉션 타입을 허용하는 메서드를 만드는 데 유용합니다.
?– 모든 타입.? extends T(upper bounded) –T혹은T의 모든 서브타입을 허용합니다. 사용: 데이터를 읽기만 할 때.? super T(lower bounded) –T혹은T의 모든 슈퍼타입을 허용합니다. 사용: 데이터를 쓰기할 때.
실전 예제
import java.util.*;
public class Wildcards {
// Aceita lista de Number OU QUALQUER SUBTIPO (Integer, Double...)
// Só consigo LER com segurança (sei que tudo ali é Number)
public static double somarLista(List lista) {
double soma = 0;
for (Number n : lista) { // Posso ler como Number
soma += n.doubleValue();
}
// lista.add(10); // ERRO! Não posso adicionar nada (não sei se é lista de Double ou Integer)
return soma;
}
public static void main(String[] args) {
List inteiros = new ArrayList<>();
inteiros.add(1);
inteiros.add(2);
List doubles = new ArrayList<>();
doubles.add(1.5);
doubles.add(2.5);
// O método aceita ambos!
System.out.println(somarLista(inteiros));
System.out.println(somarLista(doubles));
}
}
Type Erasure
Generics는 컴파일러를 위해서만 존재합니다. 컴파일이 끝난 후, Java는 (type erasure) 모든 제네릭 정보를 삭제하고 Object(또는 상한) 로 대체하며 자동으로 캐스트를 삽입합니다. 실행 시간에 List와 List는 같은 클래스(List)입니다. 이 전략은 이전 Java 버전과의 호환성을 유지하기 위해 채택되었습니다.