제네릭 자료형
- 제네릭 자료형이란? -> 클래스에서 사용하는 자료형이 여러개 일수 있고, 그 기능은 동일한 경우 클래스의 자료형을 특정하지 않고 일반화한 다음 추후 해당 클래스를 사용할 때 지정 할 수 있도록 선언
- 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 를 사용하라는 의미이다.
public void giveMeat(Cage<? extends Carnivore> cage, Meat meat) {
List<Carnivores> cs = cage.getAll();
}
Cage<? super Tiger> ctt = ct;
ctt.push(new Tiger());
제네릭 메서드
- 자료형 매개변수를 메서드의 매개변수나 반환 값으로 가지는 메서드
- 자료형 매개 변수가 하나 이상인 경우도 있음 (ex. <T, E>)
- 제네릭 클래스가 아니어도 내부에 제네릭 메서드는 구현하여 사용할 수 있음 (ex. public methodName(T a))
- 예시는 아래와 같다.
public class Point<T, V> {
T x;
V y;
Point(T x, V y){
this.x = x;
this.y = y;
}
public T getX() {
return x;
}
public V getY(){
return y;
}
}
public class GenericMethod{
public static <T, V> double makeRectangle(Point<T, V> p1, Point<T, V> p2) {
double left = ((Number)p1.getX()).doubleValue();
double right = ((Number)p2.getX()).doubleValue();
double top = ((Number)p1.getY()).doubleValue();
double bottom = ((Number)p2.getY()).doubleValue();
double width = right - left;
double height = bottom - top;
return width * height;
}
public static void main(String[] args){
Point<Integer, Double> p1 = new Point<Integer, Double>(0, 0.0);
Point<Integer, Double> p2 = new Point<>(10, 10.0);
double rect = GenericMethod.<Integer, Double>MakeRectangle(p1,p2); //사용할때 변수를 정의해 준다.
}
}
REFERENCES
- https://pathas.tistory.com/160
- 최범균님의 제네릭 변성