List list = new ArrayList();
list.add("Hello");
String str = (String) list.get(0);
=> 따라서 아래처럼 모든 타입을 받지않고, String 타입만 받겠다고 명시하면 좋다.
List<String> list = new ArrayList<String> ();
list.add("hello");
String str = list.get(0);
// 이렇게 클래스명 옆에 <T> 타입 파라미터를 명시하면 된다.
public class Box<T> {
private T t;
public T get(){
return t;
}
public void set(T t){
this.t = t;
}
}
Box<String> box = new Box<String>(); // 타입 파라미터에 들어갈 타입을 명시해준다! (String 타입으로 명시)
box.set("hello");
String str = box.get();
예제
Box 클래스는 모든 멤버들이 Object 타입이다. 따라서 Box 클래스의 멤버들을 사용시에 데이터 타입을 바꿔주고 사용해야한다.
ex) 클래스 선언부 : class Box <K, V, ... > { ... }
객체 생성부 : Box<TV, String> box1 = new Box<TV, String> ();
리턴 타입 앞에 "<>" 기호를 추가하고 타입 파라미터를 기술할 것!
=> 형태 : public <타입파라미터> 리턴타입 메소드명(매개변수, ...) { ... }
ex)
메소드 선언시에,
public Box< T > func1(T t) { ... (메소드 정의부) }
형태가 아니라
public < T > Box < T > func1(T t) { ... (메소드 정의부) }
와 같이 메소드의 리턴타입 ( Box < T > ) 앞에 반드시!! 현재 사용되는 타입 파라미터 ( = 여기서는 < T > )를 명시해줘야한다!
형태1 : 리턴타입 변수 = <변환할 타입> 메소드명(매개값);
- ex) Box< Integer > box1 = < Integer >boxing(100);
형태2 : 리턴타입 변수 = 메소드명(매개값);
- ex) Box< Integer > box1 = boxing(100);
public class Util{
public static <T> box<T> boxing(T t){ // 제네릭 메소드 boxing
Box<T> box = new Box<T>();
box.set(t);
return box;
}
}
main 메소드
Box<Integer> box1 = Util.<Intger>boxing(100); // 제네릭 메소드를 호출시 명시적으로 호출
// cf. 이때 Wrapper 클래스 중 하나인 Integer 형으로 타입을 지정.
// 지정된 타입이 Integer객체 타입인데, 매개변수로 그냥 정수 100을 넘김. 이러면 자동으로 int 타입 100이 Integer 클래스 안으로 들어감!
int intValue = box1.get(); // 리턴되는 타입이 Integer 객체인데, 이를 그냥 int 형으로 받을수있다.
Box<String> box2 = Util.boxing("홍길동"); // 제네릭 메소드를 호출시 이처럼 <변환할 타입> 부분을 생략하고 호춣해도 된다.
Stirng strValue = box2.get();
Box<int> box2 = Util.<Integer>boxing(2); => 에러발생!!!!
// 제네릭에서 타입 파리미터는 객체 타입만 할당가능. 기본 타입이 불가능하다!
// 이런 경우를 위해 Wrapper 클래스를 지원하는 것!
제네릭에서 타입 파리미터는 객체 타입만 할당가능. 기본 타입이 불가능하다!
=> 이런 경우를 위해 Wrapper 클래스를 지원하는 것!
예제
타입 파리미터로 구체적인 타입(ex. Integer) 외에도 상속 및 구현 관계를 이용해 타입 명시가능
타입 파라미터에 지정될 타입이 제한된다.
형태 : public <T extends 부모클래스타입(or 인터페이스 타입)> 리턴타입 메소드(매개변수) {...(메소드 정의부)}
- ex) public < T extends Box > int compare(T t1, T t2){ ... }
타입 제한기능 : 해당 클래스를 상속받을 수 있는 타입만 지정가능.
ex) 상속받을 클래스로 Number 클래스를 지정한경우
=> public < T extends Number > int compare(T t1, T t2){...}
타입 T 는 반드시 Number 클래스를 상속받은 타입 or Number 클래스 타입이여야한다.
이런 함수에서 T 타입을 Number 클래스를 상속받은 타입으로 지정안하면 Number 클래스의 메소드인 doubleValue() 메소드를 가지고 있지 않기 떄문에 에러가 발생!
제네릭 메소드에서 상속관계로 타입을 제한하듯이, 와일드타입은 제네릭 메소드말고 일반 메소드에서 타입을 제한할 때 사용
일반 메소드에 활용되므로, 메소드 안에 T, V 같은 변수없고 대신에 물음표 ? 사용
- 내가 매개변수로 받는 타입이 제네릭 타입일때 사용
일반 메소드( <?> ) {
}
해당 제네릭 타입이 (클래스로 만들어진 제네릭 타입) ( ex. class < T > )
타입 파라미터( T ) 에 어떤 타입을 할당해서 만들어진 타입이든 간에,
클래스를 활용해 만들어진 제네릭 타입이기만 하면,
일반 메소드의 매개변수 에서 매개변수 타입으로 무조건 다 받아들이겠다는 의미!예를들어, box < T > 라는 제네릭 타입라는 타입은 클래스 box 를 이용해서 만들어진 타입이다. 이렇게 클래스를 활용해 만들어지는 타입은 box< Integer >, box< Character >, box< Float > , ... 등이 있는데 이렇게 모든 타입을 다 받겠다는 의미이다!
형태1 : 제네릭타입<?>
- 클래스를 활용한 제네릭 타입이기만 하면 일반 메소드의 매개변수 타입으로 지정해준다!
형태2 : 제네릭타입<? extends 부모타입> (
=> 상위 클래스 제한
- 타입 파라미터를 대치하는 구체적인 타입이 부모타입을 상속받은 타입이기만 하면, 일반 메소드의 매개변수 타입으로 지정해준다!
형태3 : 제네릭타입<? super (자식타입)>
=> 타입 파라미터를 대치하는 구체적인 타입이 자식타입의 부모타입이기만 하면, 일반 메소드의 매개변수 타입으로 지정해준다!
우선 위와 같은 상속구조가 있다.
Worker, Student 클래스는 Person 을 상속받았고, HighStudent 는 Student 를 상속받았다.
또, 위와 같인 Course 라는 클래스를 활용한 제네릭 타입이 있다.
cf. 타입파라미터를 사용해서 배열을 생성할때, Object 타입을 사용해서 배열을 만들고, 만든 배열을 타입파라미터 타입으로 변환해줘야 한다.
stduents = (T[])( (new Object[capacity]); // Object 타입으로 생성한 배열의 타입을 T[] 타입으로 형변환
그리고 위와 같은 실행코드가 있다.
살펴보니까, 일반 메소드 registerCourse, registerCourseStudent, registerCourseWorker 가 있다.
1.registerCourse 메소드
2.registerCourseStudent 메소드
Course 라는 제네릭 타입을 가지고 있는 녀석을 매개변수로 받긴 하되,
그 Course 라는 클래스를 활용한 제네릭 타입을 만들때 활용한 타입파라미터 타입(T) 가 Student 또는 Student 를 상속받은 제네릭 타입이여야지 매개변수 타입으로 설정해줄 수 있다.
다시말해서 Course< Person >, Course< Worker >, Course < Student >, Course < HighStudent > 타입중에 일부 타입만 받을 수 있도록 제한한다.
즉, Student 타입과, Student 를 상속받은 타입인 HighStudent 타입에 대해서 만들어진 제네릭 타입만 받을 수 있다!
// 1.일반 메소드 registerCourse 의 매개변수 타입은
// Course 라는 제네릭 타입인데, 어떤 타입이든지 다 받을 수 있다!
// 따라서 아래 메소드는 모두 다 실행된다
registerCourse(personCourse);
registerCourse(workerCourse);
registerCourse(studentCourse);
registerCourse(highStudentCourse);
// 2.Course 라는 제네릭 타입을 받을 수 있되,
// Student 타입 또는 Student를 상속받은 타입만 받을 수있도록 제한한다!
// registerCourseStudent(personCourse);
// registerCourseStudent(workerCourse);
registerCourseStudent(studentCourse);
registerCourseStudent(highStudentCourse);
public class ChildProduct< T, M > extends Product< T, M > {...}
public class ChildProduct< T, M, C > extends Product< T, M > {...}