📌 제네릭(Generic)
결정되지 않은 타입을 파라미터로 처리하고 실제 사용할 때 파라미터를 구체적인 타입으로 대체시키는 기능
<T>
: T는 타입 파라미터로, 타입이 필요한 자리에 T 사용할 수 있음을 알려주는 역할
타입 파라미터로 명시한 타입은 객체가 생성될 시점에 다른 타입으로 대체됨
타입 파라미터를 대체하는 타입은 클래스 및 인터페이스 (int(기본 타입) 불가 => Integer(포장 클래스) 가능)
변수를 선언할 때와 동일한 타입으로 호출하고 싶으면 생성자 호출 시 생성자에는 타입을 명시하지 않고 <>만 붙일 수 있음. => Box<String> box = new Box<>();
가능 (new Box<>()의 <> 안에 타입 명시 안 해도 됨)
📌 제네릭 예시
//상자 안에 어떤 게 들어갈지 처음부터 정할 수 없다! => 제네릭 이용
public class Box <T> {
public T content;
}
//(1) 박스 안에 문자열 넣기
Box<String> box = new Bow<String>();
//변수 선언 시와 같은 타입으로 호출하고 싶을 경우
//Box<String> box = new Box<>(); 가능
box.content = "박스에 문자열 넣기";
String content = box.content; //casting 없이 바로 값을 얻을 수 있음
//(2) 박스 안에 숫자 넣기
Box<Integer> box = new Box<Integer>(); //Box<int> 불가!
//변수 선언 시와 같은 타입으로 호출하고 싶을 경우
//Box<Integer> box = new Box<>(); 가능
box.content = 100;
int content = box.content; //마찬가지로 casting 없이 바로 값 얻을 수 있음
📌 제네릭 타입
결정되지 않은 타입을 파라미터로 가지는 클래스와 인터페이스
선언부에 <>부호, 그 사이에 타입 파라미터
타입 파라미터는 일반적으로 대문자 알파벳 한 글자로 표현 (변수명과 동일한 규칙으로 작성은 가능)
외부에서 제네릭 타입을 사용하려면 타입 파라미터에 구체적은 타입을 지정해야함
지정하지 않을 경우, 암묵적으로 Object 타입 사용
타입 파라미터는 기본적으로 Object 타입으로 간주되어 Object의 메소드 호출 가능 (equals()...)
public class 클래스명<A, B, ...> {...}
public interface 인터페이스명<A, B, ...> {...}
📌 클래스 제네릭 타입 선언 예시
//제네릭 타입 클래스
@Getter
@Setter //setter는 지양합시다! 예시라서 편하게 쓰기 위해 추가
public class Product<K, M> {
private K kind;
private M model;
}
public class GenericExample {
public static void main(String[] args) {
//Tv 클래스가 있을 경우
Product<Tv, String> product1 = new Product<>;
product1.setKind(new Tv());
product1.setModel("스마트 TV");
//Car 클래스가 있을 경우
Product<Car, String> product1 = new Product<>;
product1.setKind(new Car());
product1.setModel("SUV 자동차");
}
}
📌 인터페이스 제네릭 타입 선언 예시
//제너릭 타입 인터페이스
public interface Rentable<P> {
P rent();
}
//Home 클래스가 있을 경우
public class HomeAgency implements Rentable<Home> {
@Override
public Home rent() {
return new Home();
}
}
//Car 클래스가 있을 경우
public class CarAgency implements Rentable<Car> {
@Override
public Car rent() {
return new Car();
}
}
public class GenericExample {
public static void main(String[] args) {
HomeAgency homeAgency = new HomeAgency();
Home home = homeAgency.rent();
CarAgency carAgency = new CarAgency();
Car car = carAgency.rent();
}
}
📌 제네릭 메소드
//<A, B, ...> => 타입 파라미터 정의
public <A, B, ...> 리턴타입 메소드명(매개변수, ...) {...}
📌 제네릭 메소드 예시
public class Box<T> {
private T t;
public T get() {
return t;
}
public void set(T t) {
this.t = t;
}
}
public class GenericExample {
public static <T> Box<T> boxing(T t) {
Box<T> box = new Box<T>();
box.set(t);
return box;
}
public static void main(String[] args) {
//제네릭 메소드 호출 (1)
Box<Integer> box1 = boxing(100);
int intValue = box1.get();
//제네릭 메소드 호출 (2)
Box<Sting> box2 = boxing("백");
String strValue = box2.get();
}
📌 제한된 타입 파라미터(bounded type parameter)
public <T extends 상위타입> 리턴타입 메소드(매개변수, ...) {...}
📌 와일드카드 타입 파라미터
?(와일드카드)
를 사용할 수 있음?
는 범위에 있는 모든 타입으로 대체할 수 있다는 표시//명시된 클래스의 자식 클래스까지만 가능하도록
리턴타입 메소드명(제네릭타입<? extends 클래스> 변수){...}
//명시된 클래스의 부모 클래스까지만 가능하도록
리턴타입 메소드명(제네릭타입<? super 클래스> 변수){...}
//어떤 타입이든 가능하도록
리턴타입 메소드명(제네릭타입<?> 변수){...}