일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- 미국주식
- 무디스
- 금리인하
- 주린이
- 기업분석
- StringBuffer
- Java
- 다형성
- 금리인상
- 오버라이딩
- 자바
- XLF
- FCF
- 주식
- 백준
- etf
- 잉여현금흐름
- 인플레이션
- object
- 프로그래머스
- mco
- 그리디 알고리즘
- 배당성장
- 접근제어자
- 현금흐름표
- 제태크
- 객체지향
- S&P500
- javascript
- 알고리즘
- Today
- Total
오늘의하루
[Java] 제네릭(Generics) 정의 선언 사용 제한 메서드 형변환 본문
Generics 정의
컴파일 시 타입을 체크해 주는 기능으로 객체의 타입 안정성을 높이고 형변환의 번거로움을 줄여준다.
Generics 선언
클래스를 작성할 때 Object타입 대신 T와 같은 타입변수를 사용한다.
class Box{
Object item;
void setItem(Object item){this.item = item;}
Object getItem(){return item;}
}
// Generics 클래스의 선언
class Box<T>{
T item;
void setItem(T item){this.item = item;}
T getItem(){return item'}
}
참조변수, 생성자에 T대신 실제 타입을 지정하면 형변환을 생략할 수 있다.
Box<String> b = new Box<String>(); // 타입 T 대신, 실제 타입을 지정한다.
b.setItem(new Object()); // Error! String외에 다른 타입은 지정 불가
b.setItem("ABC"); // OK
String item = (String)b.getItem();
String item = b.getItem(); // 형변환 생략 가능
Generics 용어
Box<T> : 제네릭 클래스( "T의 Box" or "T Box" )
T : 타입 변수 또는 타입 매겨변수 (T는 타입 변수)
Box : 원시타입(raw type)
class Box<T>{}
// - Box<T> : 제네릭 클래스
// - Box : 원시타입
Box<String> b = new Box<String>();
// <String> : 대입된 타입 (매개변수화 타입)
// Box<String> : 제네릭 타입 호출
Generics 제한
staitc 멤버에는 타입 변수 T를 사용할 수 없다.
class Box<T>{
static T item; // Error!
static int compare(T t1, T t2){} // Error!
}
제네릭 타입의 배열을 선언하는 건 가능하지만 생성하는 건 불가능하다.
class Box<T>{
T[] itemArr; // OK 제네릭 타입의 배열 선언은 가능하다.
T[] toArray(){
T[] tmpArr = new T[itemArr.length]; // Error! 제네릭 타입의 배열 생성을 불가능
}
}
Generics 클래스의 객체 생성과 사용
제네릭 클래스 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();}
}
Box<T>의 객체 생성
- 참조변수와 생성자에 대입된 타입이 일치해야한다.
Box<Apple> appleBox = new Box<Apple>(); // OK
Box<Apple> appleBox = new Box<Grape>(); // Error!
Box<Fruit> fruitBox = new Box<Apple>(); // Error!
두개의 제네릭 클래스가 상속관계이고 대입된 타입이 일치하는 것은 가능하다.
class Box<T>{}
class FruitBox<T> extends Box<T>{}
Box<Apple> appleBox = new FruitBox<Apple>(); // OK 다형성
Box<Apple> appleBox = new Box<>(); // ok JDK 1.7부터 생략가능
대입된 타입과 다른 타입의 객체는 추가할 수 없다.
Box<Apple> appleBox = new Box<Apple>();
appleBox.add(new Apple()); // OK
appleBox.add(new Grape()); // Error! Box<Apple>에는 Apple객체만 추가 가능
제한된 Generics 클래스
제네릭 타입에 "extends"를 사용하면 특정 타입을 포함한 자식타입들을 대입할 수 있게 제한 할 수 있다.
class FruitBox<T extends Fruit>{ // Fruit를 포함한 자식 타입들이 들어올 수 있다.
ArrayList<T> list = new ArrayList<T>();
void add(T item){list.add(item);}
}
add()의 매개변수의 타입 T도 Fruit와 그 자식 타입이 될 수 있다.
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
fruitBox.add(new Apple()); // OK Apple은 Fruit를 상속받고 있다.
fruitBox.add(new Grape()); // OK Grape은 Fruit를 상속받고 있다.
fruitBox.add(new Fruit()); // OK
인터페이스의 경우에도 "implements"가 아닌 "extends"를 사용한다.
- Fruit & Eatable은 Fruit와 그의 자식 타입이면서 Eatable를 구현한 타입만 올수 있게 된다.
interface Eatable{}
class FruitBox<T extends Eatable>{}
class FruitBox<T extneds Fruit & Eatable>{}
와일드 카드 <?>
제네릭 타입에 와일드 카드를 쓰면 아무 타입을 대입할 수 있게 된다.
- 단, 와일드 카드에서는 <? extends Fruit & Eatable> 같은 " & "를 사용할 수 없다.
<? extends T> : T와 그의 자식 타입들만 가능
<? super T> : T와 그의 부모 타입들만 가능
<?> : 제한없이 모든 타입이 가능
String 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>();
System.out.println(Juice.makeJuice(fruitBox)); // OK Fruit 타입
System.out.println(Juice.makeJuice(appleBox)); // OK Apple 타입
Generics 메서드
반환타입 앞에 제네릭 타입이 선언되 메서드를 말한다.
static <T> void sort(List<T> list, Comparator<? super T> c){}
클래스의 타입 매개변수 <T>와 메서드의 타입매개변수 <T>는 별개이다.
class FruitBox<T>{
static <T> void sort(List<T> list, Comparator<? super T> c){}
}
제네릭 메서드를 호출할 때 타입 변수에 타입을 대입해야 한다.
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();
System.out.println(Juice.<Fruit>makeJuice(fruitBox));
System.out.println(Juice.makeJuice(appleBox)); // 대입된 타입 생략 가능
System.out.println(this.<Fruit>makeJuice(fruitBox)); // OK
System.out.println(<Fruit>makeJuice(fruitBox)); // Error! 클래스 이름 생략 불가능
Generics 타입의 형변환
제네릭 타입과 원시 타입간의 형변환은 하지 않는것이 좋다.
- JDK 1.5 이후 모두 제네릭 타입으로 바뀌었기 때문이다.
Box box = null;
Box<Object> objBox = null;
box = (Box)objBox; // OK 제네릭 타입 -> 원시 타입 BUT 경고 발생
objBox = (Box<Object>)box; // OK 원시 타입 -> 제네릭 타입 BUT 경고 발생
와일드 카드가 사용된 제네릭 타입으로 는 형변환이 가능하다.
Box<? extends Object> wBox = new Box<String>();
FruitBox<? extends Fruit> box = null;
FruitBox<Apple> appleBox = (FruitBox<Apple>) box; // OK BUT 미확인 타입으로 형변환 경고 발생
<? extends Object>를 줄여서 <?>로 쓸 수 있다.
Optional<?> EMPTY = new Optional<?>(); // Error! 미확인 타입의 객체는 생성 불가
Optional<?> EMPTY = new Optional<Object>(); // OK
Optional<?> EMPTY = new Optional<>(); // OK 위와 같은 문장이다.
Generics 타입의 제거
- 컴파일러는 제네릭 타입을 제거하고 필요한 곳에 형변환을 넣는다.
1. 제네릭 타입의 경계(bound)를 제거한다.
class Box<T extends Fruit>{
void add(T t){}
}
// 경계(bound) 제거
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] 비트 연산자(AND)로 홀수 짝수 구분하기 (0) | 2022.08.30 |
---|---|
[Java Note] 객체지향 개념 기초 요약본 (0) | 2022.08.29 |
[Java]제한된 Generic 클래스 (0) | 2022.08.25 |
Java로 만든 RPG 게임 (0) | 2022.08.23 |
Java로 만든 스케줄 관리 프로그램 (0) | 2022.08.23 |