| 타입 | 설명 |
|---|---|
| T | Type |
| E | Element |
| K | Key |
| V | Value |
| N | Number |
public class ClassName <T> { ... }
public Interface InterfaceName <T> { ... }
public class ClassName <T , K> { ... }
public class Main {
public static void main(String[] args) {
// 해당 클래스를 생성할 경우, 구체적인 타입 명시
ClassName<String, Integer> a = new ClassName<String, Integer>();
}
}
제네릭 클래스 선언
class ClassName<E> {
private E element ;
void set(E element){
this.element = element;
}
E get(){
return element;
}
}
Main 클래스
class Main{
public static void main(String[] args){
ClassName<String> a = new ClassName<String>();
ClassName<Integer> b = new ClassName<Integer>();
a.set("10");
b.set(10);
System.out.println("a data : " + a.get());
// 반환된 변수의 타입 출력
System.out.println("a E Type : " + a.get().getClass().getName());
System.out.println("b data : " + b.get());
// 반환된 변수의 타입 출력
System.out.println("b E Type : " + b.get().getClass().getName());
}
}
/* RESULT */
a data : 10
a E Type : java.lang.String
b data : 10
b E Type : java.lang.Integer
제네릭 클래스
class ClassName<K, V> {
private K key; // K 타입
private V value; // V 타입
void set(K key , V value){
this.key = key;
this.value = value;
}
K getKey() {
return key;
}
V getValue() {
return value;
}
}
Main 클래스
class Main {
public static void main(String[] args) {
ClassName<String, Integer> a = new ClassName<String, Integer>();
a.set("10", 10);
System.out.println(" Key data : " + a.getKey());
// 반환된 변수의 타입 출력
System.out.println(" K Type : " + a.getKey.getClass().getName());
System.out.println(" Value Data : "+ a.getValue());
// 반환된 변수의 타입 출력
System.out.println(" V Type : " + a.getSecond().getClass().getName());
}
}
/* RESULT */
Key data : 10
K Type : java.lang.String
Value data : 10
V Type : java.lang.Integer
public <T> T genericMethod(T o){
...
}
class ClassName<E> {
private E element;
void set(E element) {
this.element = element;
}
E get(){
return element;
}
// 아래 메소드의 E 타입은 제네릭 클래스의 E 타입과 다른 독립적인 타입
static <E> E genericMethod1(E o) {
return o;
}
static <T> T genericMethod2(T o){
return o;
}
}
public class Main {
public static void main(String[] args) {
// 제네릭 메소드1 Integer
System.out.println("<E> returnType : " + ClassName.genericMethod1(3).getClass().getName());
// 제네릭 메소드1 String
System.out.println("<E> returnType : " + ClassName.genericMethod1("ABCD").getClass().getName());
// 제네릭 메소드2 ClassName a
System.out.println("<T> returnType : " + ClassName.genericMethod1(a).getClass().getName());
// 제네릭 메소드2 Double
System.out.println("<T> returnType : " + ClassName.genericMethod1(3.0).getClass().getName());
}
}
<K extends T> // T와 T의 자손 타입만 가능. (K는 외부에서 지정하는 타입)
<K super T> // T와 T의 부모(조상) 타입만 가능. (K는 외부에서 지정하는 타입)
<? extends T> // T와 T의 자손 타입만 가능
<? super T> // T와 T의 부모(조상) 타입만 가능
<?> // 모든 타입 가능. <? extends Object > 와 같은 의미
/*
Number 와 이를 상속하는 Integer, Short, Dobule, Long 등의
타입이 지정될 수 있고, 객체 혹은 메소드를 호출 할 경우 K는
지정된 타입으로 변환
*/
<K extends Number>
/*
Number 와 이를 상속하는 Integer, Short, Double, Long 등의
타입이 지정될 수 있고, 객체 혹은 메소드를 호출 할 경우 지정되는 타입이 없어
타입 참조를 할 수 는 없다
*/
<? extends T > // T와 T의 자손 타입만 가능
// T 타입을 포함한 자식(자손) 타입만 가능
< T extends B > // B와 C타입만 올 수 있음
< T extends E > // E 타입만 올 수 있음
< T extends A > // A, B, C, D, E 타입이 올 수 있음
< ? extends B > // B와 C타입만 올 수 있음
< ? extends E > // E 타입만 올 수 있음
< ? extends A > // A, B, C, D, E 타입이 올 수 있음
// Integer, Long, Byte, Double, Float, Short 같은 수를 표현하는 래퍼 클래스로만 제한
public class ClassName <K extends Number> { ... }
public class Main {
public static void main(String[] args) {
ClassName<Double> a1 = new ClassName<Double>(); // OK!
ClassName<String> a2 = new ClassName<String>(); // Bound mismatch
}
}
<K super B> // B와 A타입만 올 수 있음
<K super E> // E, D, A 타입만 올 수 있음
<K super A> // A타입만 올 수 있음
<? super B> // B와 A타입만 올 수 있음
<? super E> // E, D, A 타입만 올 수 있음
<? super A> // A 타입만 가능
extends 뒤에오는 타입이 최상위 타입이 되고, 해당 타입과 그에 대한 하위 타입, 역으로 생각해보면 E 객체는 반드시 Comparable을 구현해야한다는 의미
public class ClassName<E extends Comparable<? super E>>{ ... }
// Example
public class SaltClass <E extends Comparable<E>> { ... }
public class Student implements Comparable<Student> {
@Override
public int compareTo(Person o) { ... };
}
public class Main{
public static void main(String[] args) {
SaltClass<Student> a = new SaltClass<Student>();
}
}
Comparable 가 아닌 <? super E> 인 이유
- super E 는 E를 포함한 상위 타입 객체들이 올 수 있음
- Person을 상속받고 Comparable 구현부인 comparTo 에서 Person 타입으로 업캐스팅 한다면 아래 상황이 발생
- <E extends Comparable> 라면 SaltClass a 객체가 타입 파라미터로 Student 를 주지만, Comparable 에서는 그보다 상위 타입인 Person으로 비교하기 때문에 Comparable 의 E인 Student보다 상위 타입 객체이기 때문에 제대로 정렬이 안되거나 에러가 날 수 있음
- 그렇기 때문에 E 객체의 상위 타입, 즉 <? super E> 을 해줌으로써 위와 같은 불상사를 방지할 수가 있음
- 즉 <E extends Comparable<? super E>> 는 E 타입 또는 슈퍼클래스가 Comparable 을 의무적으로 구현해야한다는 뜻으로 슈퍼 클래스 타입으로 업캐스팅이 발생하더라도 안정성을 보장
public class SaltClass <E extends Comparable<E>> { ... } // Error 가능성
public class SaltClass <E extends Comparable<? super E>> { ... } // 안전성이 높음
public class Person { ... }
public class Student extends Person implements Comparable<Person> {
@Override
public int compareTo(Person o) { ... };
}
public class Main{
public static void main(String[] args){
SaltClass<Student> a = new SaltClass<Student>();
}
}