일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- javascript
- 프로그래머스
- 백준
- 제태크
- 오버라이딩
- 미국주식
- etf
- 금리인하
- 주식
- 배당성장
- 접근제어자
- 현금흐름표
- 알고리즘
- Java
- 주린이
- mco
- S&P500
- 인플레이션
- XLF
- 잉여현금흐름
- 객체지향
- object
- StringBuffer
- 금리인상
- FCF
- 그리디 알고리즘
- 자바
- 다형성
- 기업분석
- 무디스
- Today
- Total
오늘의하루
[Java Note] Generics(제네릭스) 선언 정의 제한 형 변환 와일드 카드 본문
제네릭스는 컴파일 시 타입을 체크해주는 기능으로 객체의 타입 안정성을 높이고 형 변환의 번거로움을 줄여줌으로써 코드가 간결해질 수 있다.
제네릭 클래스 선언
클래스를 작성할 때 Object 타입 대신 T와 같은 타입 변수를 사용한다.
class TEST{
Object item;
void setItem(Object item){this.item = item;}
Object getItem(){return item;}
}
// Object 대신 T와 같은 타입변수 사용
class Test<T>{
T item;
void setItem(T item){this.item = item;}
T getItem(){return item;}
}
- 참조 변수, 생성자에 T 대신 실제 타입을 지정하면 형 변환 생략이 가능하다.
Test<String> b = new Test<String>();
b.setItem(new Object()); // Error! String타입만 지정 가능하다.
b.setItem("ABC");
String item = b.getItem(); // 형변환 필요없음
// String item = (String)b.getItem(); 생략가능
제네릭스 용어
class Test<T> | Box = 원시타입 (raw type) |
Box<T> = 제네릭 클래스 |
Test<String> b = new Test<String>(); | String = 대입된 타입 |
Test<String> = 제네릭 타입 호출 |
제네릭스의 제한
1. static 멤버에는 타입 변수 T를 사용할 수 없다.
class Test<T>{
static T item; // Error!
static T getItem(){...} // Error!
}
2. 제네릭 타입의 배열 T []를 선언할 수는 있지만 생성할 수 없다.
class Test<T>{
T[] itemArr; // OK 선언은 가능하다.
T[] toArray(){
T[] tmpArr = new T[itemArr.length]; // Error! 생성은 불가능하다.
return tmpArr;
}
}
제네릭 클래스의 객체 생성과 사용
제네릭 클래스 Box <T> 선언
class Box<T>{
ArrayList<T> list = new ArrayList<T>();
void add(T item){list.add(item);}
T get(int i){return list.get(i);}
ArrayList<T> getList(){return list;}
int size(){return list.size();}
public String toString(){return list.toString();}
}
1. 참조 변수와 생성자에 대입된 타입이 일치해야 한다.
Box<Apple> apple = new Box<Apple>(); // OK
Box<Apple> apple = new Box<Grape>(); // Error! 타입 불일치
Box<Fruit> apple = new Box<Apple>(); // Error! 타입 불일치
2. 제네릭 클래스 간 상속관계이고 대입된 타입이 일치한다면 생성 가능
Box{}
FruitBox extends Box{}
Box<Apple> appleBox = new FruitBox<Apple>(); // OK 다형성
Box<Apple> appleBox = new Box<>(); // OK JDK1.7부터 생성자에 대입되는 타입 생략 가능
3. 대입된 타입과 다른 타입의 객체는 추가할 수 없다.
Box<Apple> appleBox = new Box<Apple>();
appleBox.add(new Apple()); // OK
appleBox.add(new Grape()); // Error! Apple타입만 가능하다.
제한된 제네릭 클래스
1. 제네릭 타입에 "extends"를 사용하면 특정 타입을 포함한 자식들만 대입할 수 있다.
class FruitBox<T extends Fruit>{ // Fruit와 Fruit자식만 타입으로 올수 있다.
ArrayList<T> list = new ArrayList<T>();
void add(T item){list.add(item);}
}
2. 매개변수 타입 T도 Fruit와 Fruit의 자식 타입이 될 수 있다.
// 1번의 코드에서 이어진다.
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
fruitBox.add(new Apple()); // OK Apple은 Fruit의 자식
fruitBox.add(new Grape()); // OK Grape은 Fruit의 자식
3. 인터페이스의 경우에도 제네릭 타입으로 지정할 때는 "extends" 사용한다.
interface Eatable{}
class FruitBox<T extends Eatable>{...}
4. 상속받는 타입과 인터페이스 구현한 타입을 동시에 만족시키는 제네릭 타입
class FruitBox <T extends Fruit & Eatable>{....}
와일드카드 "? "
제네릭 타입에 와일드카드를 쓰면 여러 타입의 대입이 가능하다.
- 단 와일드카드에서는 <? extends Fruit & Eatable> 같은 " & "를 사용하지 못한다.
구분 | 특징 |
< ? extends T > | T와 그의 자식 타입만 가능 |
< ? super T > | T와 그의 부모 타입만 가능 |
< ? > | 모든 타입 가능 |
class Juicer{
staitc Juice makeJuice(FruitBox<? extends Fruit> box){
String tmp = "";
for(Fruit f : box.getList()){
tmp = tmp + f + " ";
}
return new Juice(tmp);
}
}
- makeJuice의 매개변수로는 FruitBox <Apple>, FruitBox <Grape> 가능하다.
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();
Juicer.makeJuice(fruitBox); // OK
Juicer.makeJuice(appleBox); // OK
제네릭 메서드
1. 반환 타입 앞에 제네릭 타입이 선언된 메서드
static <T> void sort(List<T> list, CompareTo<? super T> c)
2. 클래스의 타입 매개변수 <T>와 메서드의 타입 변수 <T>는 별개이다.
clas FruitBox<T>{
static <T> void sort(List<T> list, CompareTo(? super T> c){}
}
3. 제네릭 메서드를 호출할 때 타입 변수에 타입을 대입해야 한다.
- 대부분의 경우 추정이 가능하므로 생략할 수 있다.
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();
Juicer.<Fruit>makeJuice(fruitBox);
Juicer.<Apple>makeJuice(appleBox);
Juicer.makeJuice(appleBox); // 생략가능
제네릭 타입의 형 변환
1. 제네릭 타입과 원시 타입 간의 형 변환은 불가능하다.
Box box = null;
Box<Object> objbox = null;
box = (Box)objbox; // OK But 경고 발생 (제네릭 타입을 원시 타입으로 변환)
objbox = (Box<Object>)box // OK But 경고 발생 (원시 타입을 제네릭 타입으로 변환)
2. 와일드카드가 사용된 제네릭 타입으로 형 변환 가능
Box<? extends Object> wBox = new Box<String>();
FruitBox<? extends Fruit> box = null;
FruitBox<Apple> appleBox = (FruitBox<Apple>)box; // OK But 미확인 타입으로 형변환 경고
3. <? extends Object >를 줄여서 <? >로 쓸 수 있다.
Optional<?> EMPTY = new Optional<?>(); // Error! 미확인 타입의 객체는 생성 불가
Optional<?> EMPTY = new Optional<Object>(); // OK
Optional<?> EMPTY = new Optional<>(); // OK <Object>와 같은 문장이다.
제네릭 타입의 제거
컴파일러는 제네릭 타입을 제거하고 필요한 곳에 형 변환을 넣는다.
1. 제네릭 타입의 경계를 제거
class Box<T extends Fruit>{
void add(T t){}
}
// 제네릭 타입의 경계를 제거
class Box{
void add(Fruit t){}
}
2. 제네릭 타입 제거 후 타입이 불일치하면 형 변환을 추가한다.
T get(int i){
return list.get(i);
}
// 형변환 추가
Fruit get(int i){
return (Fruit)list.get(i);
}
3. 와일드카드가 포함된 경우 적절한 타입으로 형 변환을 추가한다.
static Juice makeJuice(FruitBox<? extends Fruit> box){
String tmp = "";
for(Fruit f : box.getList()){
tmp = tmp + f + " ";
}
return new Juice(tmp);
}
// 와일드 카드이기 때문에 적절한 타입으로 형변환
static Juice makeJuice(FruitBox box){
String tmp = "";
Iterator it = box.getList().iterator();
while (it.hasNext()){
tmp = tmp + (Fruit)it.next() + " ";
}
return new Juice(tmp);
}
'JAVA' 카테고리의 다른 글
[Java] Process & Thread (프로세스와 스레드) 알아보기 (0) | 2022.09.05 |
---|---|
[Java] 표준, 메타 Annotation(애너테이션) (0) | 2022.09.02 |
[Java] Enum (열거형), Annotation (애너테이션) (0) | 2022.08.31 |
[Java] 비트 연산자(AND)로 홀수 짝수 구분하기 (0) | 2022.08.30 |
[Java Note] 객체지향 개념 기초 요약본 (0) | 2022.08.29 |