제네릭 자료형이란? -> 클래스에서 사용하는 자료형이 여러개 일수 있고, 그 기능은 동일한 경우 클래스의 자료형을 특정하지 않고 일반화한 다음 추후 해당 클래스를 사용할 때 지정 할 수 있도록 선언
java 5 부터 지원되었다.
실제 사용되는 자료형의 변환은 컴파일러에 의해 검증되므로 안정적인 프로그램 방식이다.
컬렉션 프레임워크에서 많이 사용된다.
예시는 아래와 같다.
public class GenericPrinter<T> {
private T member;
public void setMember (T member){
this.member = member;
}
public T getMember(){
return member;
}
}
public class test {
public static void main(String[] args){
Member member = new Member();
GenericPrinter<Member> memberPrinter = new GenericPrinter<>(); //타입 지정
memberPrinter.setMember(member);
Member member = memberPrinter.getMember();
}
}
자료형 매개변수 T(type parameter): 이 클래스를 사용하는 시점에 사용할 자료형을 지정한다.
static 변수는 사용할 수 없다.
GenericPrinter: 제네릭 자료형
E : element, K: value
T, E, K 외에 아무 문자로 지정할 수 있지만 의미있게 지정하는게 좋다.
<T extends 클래스> 사용하기
T 자료형의 범위를 제한 할 수 있다.
클래스를 상속받는 자료형만 T 자리에 들어올 수 있다. 즉, T 자리엔 해당 클래스이거나, 자식들이 들어갈 수 있다.
상위 클래스에서 선언하거나 정의하는 메서드를 활용할 수 있다.
상속을 받지 않는 경우 T는 Object클래스가 기본으로 제공하는 메서드만 사용가능
<T extends 클래스 > -> 해당 클래스에서 상속받은 클래스만 T 자리에 올 수 있다.
<T extends Member> -> Member를 상속 받은 Person1, Person2만 T 자리에 올 수 있다.
T extends를 사용한 프로그래밍
GenericPrinter에 member 변수의 자료형을 상속받아 구현.
T에 무작위 클래스가 들어갈 수 없게 member변수를 상속받은 클래스로 한정.
예시는 아래와 같다.
public class GenericPrinter<T extends Member> {
private T member;
public void setMember (T member){
this.member = member;
}
public T getMember(){
return member;
}
}
public abstract class Member {
public abstract void doPrinting();
}
public class Person1 extends Member {
public String toString(){
return "Person1";
}
@Override
public void doPrinting(){
}
}
public class test {
public static void main(String[] args){
Member member = new Member();
GenericPrinter<Person1> memberPrinter = new GenericPrinter<>(); //Person1이 Member를 상속받고 있기때문에 쓸수있음 다른 클래스는 T자리에 쓸 수 없음
memberPrinter.setMember(member);
Member member = memberPrinter.getMember();
}
}
<? super T> 사용하기
상속관계에 존재하는 클래스만 자료형으로 받고 싶을때 사용한다.
T 의 부모이거나 T 인 자료형만 ? 자리에 올 수 있다.
super는 T에 사용이 불가하다.
<? super T> 를 사용한 프로그래밍
// Person 클래스
class Person {
String name;
Person() {
}
Person(String name) {
this.name = name;
}
public String toString() {
return name;
}
}
// Person 상속 Man 클래스
class Man extends Person {
...
}
// Person 상속 Woman 클래스
class Woman extends Person {
...
}
public class WildSuper {
public static void main(String[] args) {
// Person
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("사람"));
personList.add(new Person("인간"));
// Man
List<Man> manList = new ArrayList<Man>();
manList.add(new Man("man1"));
manList.add(new Man("man2"));
// Woman
List<Woman> womanList = new ArrayList<Woman>();
womanList.add(new Woman("woman1"));
womanList.add(new Woman("woman2"));
// printData(womanList); → Man 클래스의 상위 클래스가 아니기 때문에 메소드 호출 불가
}
// Man 클래스와 그 상위 클래스로 생성된 인스턴스만 매개변수로 전달 가능
public static void printData(List<? super Man> list) {
for (Object obj : list) {
System.out.println(obj);
}
}
}
< ? > 사용하기
< ? extends Object > 의 줄임 표현이다.
어떤 자료형의 객체도 매개변수로 받겠다는 뜻이다.
Unbounded WildCard라고 알려져 있다.
< ? > 를 사용한 프로그래밍
public class WildTest {
public static void main(String[] args) {
// List는 인터페이스이기 때문에 ArrayList 생성 후 Upcasting 이용
List<String> list = new ArrayList();
list.add("test1");
list.add("test2");
list.add("test3");
// Integer 자료형 list2 객체 생성
List<Integer> list2 = new ArrayList();
list2.add(1);
list2.add(2);
list2.add(new Integer(3));
// Double형 list3 생성
List<Double> list3 = new ArrayList();
list3.add(10.1);
list3.add(11.2);
list3.add(12.3);
// static 메소드 호출
printData(list);
printData(list2);
printData(list3);
}
// 리스트 출력 메소드
public static void printData(List<?> list) {
for (Object v : list) {
System.out.println(v);
}
}
}
extends vs super
이펙티브 자바에서는 producer-extends, consumer-super 라는 말이있다.
즉 제공할때는 extends 를 사용하고, 사용할 때는 super 를 사용하라는 의미이다.