더 자세한 것은 출처를 참고 하시면 됩니다.
아래는 제가 이해하기에 도움 되는 수준으로 정리 한 것입니다.
제네릭 클래스
public class 클래스<T> { ... }
// 가장 기본적인 형태의 제네릭 클래스
class GenericClass<T>{
private T t;
T getValue(){ // 제네릭 class 내 에서는 제네릭 타입에 대한 리턴 정의가 편하다.
return t;
}
}
// Generic class를 상속 받으면 제네릭 타입도 같이 받아야 한다.
class extendedClass<T> extends GenericClass<T>{
}
// Generic class를 상속받고, 제네릭 타입 K 도 받는 형태
class extendedClass2<T, K> extends GenericClass<T>{
private K k;
}
interface Entry<K, V>{
V getKey(K k);
void setKey(V v);
}
class implementedClass<K, V> implements Entry<K, V>{
@Override
public V getKey(K k) {
return null;
}
@Override
public void setKey(V v) {
}
}
// 제레릭 클래스를 리턴하는 함수
public <T> GenericClass<T> getGenericClass(T t){
GenericClass<T> ret = new GenericClass<>();
return ret;
}
제네릭 함수
타입파라미터가 앞에 나오기에 모르면 읽을 수 조차 없다.
public <타입파라미터> 리턴타입 메소드명(매개변수, ...) { ... }
public int original_Object(Object object){
int castedT = (Integer)object; // Object 사용시 type cast는 피할수 없다.
return castedT;
}
public <T> int Generic_Original_func(T t){
// type cast 없이 사용 가능
return 0;
}
// 파라미터 타입을 제한한 형태
public int func1(List<? extends Number> list){
return 0;
}
// 제네릭 타입을 리턴 하고 싶은데, 타입이 안 맞을 경우
public <T> T func2(String a, Integer b){
// 제네릭 타입 T와 리턴이 일치 하지 않으면 컴파일 에러가 발생한다.
// 이럴 경우 type cast가 필요하다.
return (T) (a + String.valueOf(b));
}
// 멀티 타입 파리미터 제네릭,
// + 파라미터 타입을 제한한 형태
// + 제네릭 타입 리턴
public <T, V> V func3(List<? extends V> list, T t, V v){
if(list != null)
return list.get(0); // list의 타입이 V로 확실 하기에 컴파일 에러가 발생하지 않는다.
// v의 타입이 확실히 V이기에 컴파일 에러가 발생하지 않는다.
return v;
}
// 제레릭 클래스를 리턴하는 함수
public <T> GenericClass<T> getGenericClass(T t){
GenericClass<T> ret = new GenericClass<>();
return ret;
}
와일드 카드
와일드 카드라고 표현하고 제한자라고 이해하면 적당하다.
아래는 재귀 호출을 하는 함수이다.
funcWild의 첫번째 파라미터를 Number의 파생된 파라미터만 받기 때문에, integerArrayList는 정상적으로 동작하지만,
stringArrayList를 컴파일 에러를 낸다.
public <V> int funcWild(List<? extends Number> list, V v){
ArrayList<Integer> integerArrayList = new ArrayList<>();
funcWild(integerArrayList, 3);
ArrayList<String> stringAarrayList = new ArrayList<>();
funcWild(stringAarrayList, 3); // compile error 발생
return 0;
}
하지만 아래 코드는 아무 문제 없이 돌아간다. 즉 와일드 카드는 제한자로 이해하면 된다.
조금 더 응용하면 public <V> int funcGeneric_NoWild(List<?> list, V v) 도 쓰일 수 있다.
// public <V> int funcGeneric_NoWild(List<?> list, V v){ // 이렇게 해도 문제가 없다.
public <V, K> int funcGeneric_NoWild(List<K> list, V v){
ArrayList<Integer> integerArrayList = new ArrayList<>();
funcGeneric_NoWild(integerArrayList, 3);
ArrayList<String> stringAarrayList = new ArrayList<>();
funcGeneric_NoWild(stringAarrayList, 3);
return 0;
}
와일드카드는 파라미터, 필드, 지역 변수의 타입 또는 때때로 반환 타입과 같은 다양한 상황에서 사용될 수 있습니다. 와일드 카드는 제네릭 메소드 호출에 대한 형식 인수, 제네릭 클래스 인스턴스 생성, 또는 슈퍼타입으로 사용될 수 없습니다.
제네릭타입<?> : 타입 파라미터를 대치하는 것으로 모든 클래스나 인터페이스타입이 올 수 있습니다.
제네릭타입<? extends 상위타입> : 와일드카드의 범위를 특정 객체의 하위 클래스만 올 수 있습니다.
제네릭타입<? super 하위타입> : 와일드카드의 범위를 특정 객체의 상위 클래스만 올 수 있습니다.
출처 : 나
madplay.github.io/post/java-generic-advanced
tcpschool.com/java/java_generic_various
https://offbyone.tistory.com/327
stackoverflow.com/questions/450807/how-do-i-make-the-method-return-type-generic
stackoverflow.com/questions/23349329/java-generic-return-type