변수를 선언 할 때
매번 type을 지정해야 하고,
변수의 type을 신경 써 줘야 하는
자바는 정적 타입 언어이다.
하지만 class에서 받을 매게변수에 대한
type을 미리 지정하지 않고 받을 수 있는 방법이 있다.
바로 generic
generic은 class 설계 시
매개변수의 type을 후에 지정할 수 있게 하는 keyword이다.
class Test<T> {
T item;
public Test(T item){
this.item = item;
}
}
위와 같이 class 변수명 뒤에 있는 꺽쇠(< >) 사이에
추후 type을 지정할 매개변수의 type을 임시로 넣어
(위의 경우 T)
선언이 가능하다.
또한, 꺽쇠 사이에는 여러개의 type을 넣을 수 있다.
class Main{
public static void main(String[] args){
Test<Integer> t = new Text<>(10);
}
}
사용할 때에는 위와 같이 사용한다.
이 사용법을 어디서 많이 봤다 싶었는데,
바로 ArrayList의 변수 선언이었다.
ArrayList<Integer> list = new ArrayList<>()
뭐 여튼
generic을 적용한 class 선언 때 주의점이 하나 있는데
당연하다면 당연한거지만,
static keyword를 사용할 수 없다.
일반적으로
generic을 사용하면 객체를 인스턴스화 할 때
제한 없이 아무 타입이나 지정 가능하다.
하지만,
generic class를 선언할 때 extends keyword를 사용하면,
type에 제한을 둘 수 있다.
class Parent{}
class Child extends Parent{}
class Nobody{}
class Test<T extends Parent> {
T item;
public Test(T item){
this.item = item;
}
}
class Main{
public static void main(String[] args){
Test<Parent> t1 = new Test<>();
Test<Child> t2 = new Test<>();
// Test<Nobody> t3 = new Test<>(); err
}
물론 interface로도 제한이 가능하다.
interface Inter{}
class Parent{}
class Test<T extends Parent & Inter {
...
}
&로 확장이 가능하고, class와 interface를 동시에 제한할 때
class는 interface보다 앞에 위치해야 한다.
일반적으론 위의 경우 처럼
class 전체를 generic으로 선언하지만,
어느 class의 특정 method만
generic으로 선언도 가능하다.
class Test<T>{
T t;
public Test(T t){
this.t = t;
}
static public <T> void test(T t){
...
}
}
위의 경우 class에 T type을,
method에도 T type을 받는데,
method 내에서 class변수와
같은 이름의 변수를 선언해도 상관 없는 것 처럼
(클래스 변수 != 지역 변수)
type 명이 똑같더라도, 별개의 type인 것이다.
또한, method에 static keyword 사용이 가능하다.
method에서 generic한 매개변수를 받을 때
변수의 type의 범위를 제한 하는 방법도 있다.
?라는 wildcard keyword를 써서 사용하며
3가지 범위는
class Test {
void testPrint1(ArrayList<?> arrayList){
...
}
void testPrint2(ArrayList<? super String> arrayList){
...
}
void testPrint3(ArrayList<? extends String> arrayList){
...
}
}
위에서 부터 차례대로
wildcard에 대해 이해하려고
혼자 실험을 했는데,
실험 설계(?)를 잘못해서 엄청난 고뇌에 빠졌었다...
다행히 GPT에 해당 실험이 왜 잘못되었는지 물어보면서
오류를 알게 되었고, 다시 실험을 요청해서
잘 이해하게 되었다.