// Generics
- 컴파일 시 타입을 체크해 주는 기능
- 장점: 타입 안정성 제공, 형변환할 필요 없음.
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(new Integer(3));
Integer i = list.get(0); //형변환 필요 x
- 여러 타입을 저장하고 싶으면 <Object> 사용 // 꺼낼 때 형변환 필요!
ArrayList<Object> list = new ArrayList<Object>();
list.add(10);
list.add("30");
Integer i = (Integer)list.get(0); // 형변환 필요
String i = (String)list.get(1); // 형변환 필요
class Box<T> { }
└ Box<T> : 지네릭 클래스. 'T Box' | 'T의 Box' 라고 읽음.
└ T : 타입 변수 | 타입 매개변수.
└ <> : 인자리스트
└ Box : 원시 타입
Box<String> box = new Box<String>();
└ String : 대입된 타입 (일치해야 함)
└ box : 참조변수
└ Box : 다형성에 의해 달라도 가능함. ex) List, ArrayList //왼: 조상, 오: 자식
class FruitBox<T extends Fruit>{
ArrayList<T> list = new ArrayList<T>();
...
}
└ 지네릭 타입에 extends를 사용하면, 특정 타입의 자손들만 대입할 수 있게 제한할 수 있음.
└ 인터페이스를 구현해야 한다는 제약이 필요할 때도 'implements'가 아닌 'extends'를 사용함.
- 제약:
① static 멤버에 타입 변수를 사용할 수 없다.
② 배열 생성할 때는 타입 변수를 사용할 수 없다.(new 연산자 다음에 타입 변수 올 수 x)
package com.sorrel012.generics;
public class GerericsRunner {
public static void main(String[] args) {
MyCustomList<String> list = new MyCustomList<>();
list.addElement("Element 1");
list.addElement("Element 2");
String value = list.get(0);
System.out.println(list);
System.out.println(value);
System.out.println();
MyCustomList<Integer> list2= new MyCustomList<>();
list2.addElement(Integer.valueOf(5));
list2.addElement(Integer.valueOf(7));
Integer number= list2.get(0);
System.out.println(list2);
System.out.println(number);
} //main
}
package com.sorrel012.generics;
import java.util.ArrayList;
public class MyCustomList<T> {
ArrayList<T> list = new ArrayList<>();
public void addElement(T element) {
list.add(element);
}
public void removeElement(T element) {
list.remove(element);
}
@Override
public String toString() {
return list.toString();
}
public T get(int i) {
return list.get(i);
}
}
package com.sorrel012.generics;
public class GenericsRunner {
public static void main(String[] args) {
MyCustomList<Long> list = new MyCustomList<>();
list.addElement(15L);
list.addElement(10L);
Long value = list.get(0);
System.out.println(list);
System.out.println(value);
System.out.println();
MyCustomList<Integer> list2= new MyCustomList<>();
list2.addElement(Integer.valueOf(5));
list2.addElement(Integer.valueOf(7));
Integer number= list2.get(0);
System.out.println(list2);
System.out.println(number);
} //main
}
package com.sorrel012.generics;
import java.util.ArrayList;
public class MyCustomList<T extends Number> {
ArrayList<T> list = new ArrayList<>();
public void addElement(T element) {
list.add(element);
}
public void removeElement(T element) {
list.remove(element);
}
@Override
public String toString() {
return list.toString();
}
public T get(int i) {
return list.get(i);
}
}
import java.util.ArrayList;
class Product {}
class Tv extends Product {}
class Audio extends Product{}
public class Ex01 {
public static void main(String[] args) {
ArrayList<Product> productList = new ArrayList<Product>();
ArrayList<Tv> tvList = new ArrayList<Tv>();
productList.add(new Product());
productList.add(new Tv());
productList.add(new Audio());
tvList.add(new Tv()); //tvList.add(new Product()); 불가 / tvList.add(new Audio()); 불가
printAll(productList); //printAll(tvList); 불가
} // end of main
public static void printAll(ArrayList<Product> list) {
for (Product p : list) {
System.out.println(p);
}
} //end of printAll
} //end of class
import java.util.ArrayList;
interface Eatable {}
class Fruit implements Eatable {
public String toString() { return "Fruit"; }
}
class Apple extends Fruit { public String toString() { return "Apple"; } }
class Grape extends Fruit { public String toString() { return "Grape"; } }
class Toy { public String toString() { return "Toy" ; } }
public class Ex02 {
public static void main(String[] args) {
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();
FruitBox<Grape> grapeBox = new FruitBox<Grape>();
//FruitBox<Toy> toyBox = new FruitBox<Toy>(); 에러!
fruitBox.add(new Fruit());
fruitBox.add(new Apple());
fruitBox.add(new Grape());
appleBox.add(new Apple()); //appleBox.add(new Grape()); 에러
grapeBox.add(new Grape());
System.out.println("fruitBox = " + fruitBox);
System.out.println("appleBox = " + appleBox);
System.out.println("grapeBox = " + grapeBox);
}
}
class FruitBox<T extends Fruit & Eatable> extends 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); }
int size() { return list.size(); }
public String toString() { return list.toString(); }
}
// 와일드 카드 <?> : 하나의 참조 변수로 대입된 타입이 다른 객체를 참조할 수 있다.
- <? extends T> : 와일드 카드의 상한 제한. T와 그 자손들만 가능.
- <? super T> : 와일드 카드의 하한 제한. T와 그 조상들만 가능.
- <?> : 제한 없음. 모든 타입이 가능. <? extends Object>와 동일.
ArrayList<? extends Procduct> list = new ArrayList<Product>();
ArrayList<? extends Procduct> list = new ArrayList<Tv>();
ArrayList<? extends Procduct> list = new ArrayList<Audio>();
ArrayList<? super Audio> list = new ArrayList<Product>();
ArrayList<? super Audio> list = new ArrayList<Audio>();
import java.util.ArrayList;
class Fruit2 { public String toString() { return "Fruit"; } }
class Apple2 extends Fruit2 { public String toString() { return "Apple"; } }
class Grape2 extends Fruit2 { public String toString() { return "Grape"; } }
class Juice {
String name;
Juice(String name) { this.name = name + "Juice"; }
public String toString() { return name; }
} //end of Juice class
class Juicer {
static Juice makeJuice(FruitBox2<? extends Fruit2> box) {
String tmp = "";
for(Fruit2 f : box.getList()) {
tmp += f + " ";
}
return new Juice(tmp);
}
} //end of Juicer class
public class Ex03 {
public static void main(String[] args) {
FruitBox2<Fruit2> fruitBox = new FruitBox2<Fruit2>();
FruitBox2<Apple2> appleBox = new FruitBox2<Apple2>();
fruitBox.add(new Apple2());
fruitBox.add(new Grape2());
appleBox.add(new Apple2());
appleBox.add(new Apple2());
System.out.println(Juicer.makeJuice(fruitBox));
System.out.println(Juicer.makeJuice(appleBox));
}
} // end of Ex03 class
class FruitBox2<T extends Fruit2> extends Box2<T> {}
class Box2<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(); }
} //end of Box2 class
<extends>
package com.sorrel012.generics;
import java.util.ArrayList;
import java.util.List;
public class GenericsRunner {
//입력값을 입력받은 자료형, 값 그대로 돌려줌
static <X> X doubleValue(X value) {
return value;
}
static Number sumOfNumberList(List<? extends Number> numbers) {
double sum= 0.0;
for(Number number : numbers) {
sum += number.doubleValue();
}
return sum;
}
public static void main(String[] args) {
System.out.println(sumOfNumberList(List.of(1,2,3,4,5)));
System.out.println(sumOfNumberList(List.of(1.1,2.1,3.1,4.1,5.1)));
System.out.println(sumOfNumberList(List.of(1L,2L,3L,4L,5L)));
} //main
}
package com.sorrel012.generics;
import java.util.ArrayList;
public class MyCustomList<T extends Number> {
ArrayList<T> list = new ArrayList<>();
public void addElement(T element) {
list.add(element);
}
public void removeElement(T element) {
list.remove(element);
}
@Override
public String toString() {
return list.toString();
}
public T get(int i) {
return list.get(i);
}
}
<super>
package com.sorrel012.generics;
import java.util.ArrayList;
import java.util.List;
public class GenericsRunner {
//입력값을 입력받은 자료형, 값 그대로 돌려줌
static <X> X doubleValue(X value) {
return value;
}
static void addACoupleOfValues(List<? super Number> numbers) {
numbers.add(1);
numbers.add(1.0);
numbers.add(1.0f);
numbers.add(1L);
}
public static void main(String[] args) {
List emptyList = new ArrayList<Number>();
addACoupleOfValues(emptyList);
System.out.println(emptyList);
} //main
}
//지네릭 메서드
- 클래스 타입 매개변수<T>와 메서드의 타입 매개변수<T>는 별개
- 메서드를 호출할 때마다 타입을 대입해야 한다.(대부분 생략 가능)
- 메서드를 호출할 때 타입을 생략하지 않을 때는 클래스 이름 생략 불가(드문 경우)
static <T extends Fruit> Juicer makeJuice(FruitBox<T> box) {
String tmp = "";
for(Fruit f : box.getList()) tmp += f + " ";
return new Juice(tmp);
}
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();
System.out.println(Juicer.makeJuice(fruitBox)); //타입 생략 가능
System.out.println(Juicer.makeJuice(appleBox)); //타입 생략 가능
System.out.println(Juicer.<Fruit>makeJuice(fruitBox)); //생략하지 않을경우 클래스 이름 붙이기
System.out.println(Juicer.<Apple>makeJuice(appleBox)); //생략하지 않을경우 클래스 이름 붙이기
package com.sorrel012.generics;
import java.util.ArrayList;
import java.util.List;
public class GerericsRunner {
//입력값을 입력받은 자료형, 값 그대로 돌려줌
static <X> X doubleValue(X value) {
return value;
}
static <X extends List> void duplicate(X list) {
list.addAll(list);
}
public static void main(String[] args) {
String value1 = doubleValue(new String("하이"));
Integer number1 = doubleValue(Integer.valueOf(5));
ArrayList list1 = doubleValue(new ArrayList());
System.out.println(value1);
System.out.println(number1);
System.out.println(list1);
System.out.println();
ArrayList numbers = new ArrayList<>(List.of(1,2,3));
duplicate(numbers);
System.out.println(numbers);
} //main
}
package com.sorrel012.generics;
import java.util.ArrayList;
public class MyCustomList<T extends Number> {
ArrayList<T> list = new ArrayList<>();
public void addElement(T element) {
list.add(element);
}
public void removeElement(T element) {
list.remove(element);
}
@Override
public String toString() {
return list.toString();
}
public T get(int i) {
return list.get(i);
}
}
// 지네릭 타입의 형변환
- 지네릭 타입과 원시 타입의 형변환은 바람직하지 않다.(경고 발생)
- 와일드 카드가 사용된 지네릭 타입으로는 형변환 가능!
Box box = null;
Box<Object> objBox = null;
box = (Box)bojBox; //지네릭 → 원시. 가능하지만 경고 발생
objBox = (Box<Object>)box; //원시 → 지네릭. 가능하지만 경고 발생
FruitBox<? extends Fruit> box = new FruitBox<Fruit>();
FruitBox<? extends Fruit> box = new FruitBoxApple>();
// 지네릭 타입의 제거
① 지네릭 타입의 경계를 제거
② 지네릭 타입을 제거한 후 타입이 일치하지 않으면 형변환 추가
'자바(JAVA)' 카테고리의 다른 글
[자바(Java)] 애너테이션 (0) | 2022.12.17 |
---|---|
[자바(Java)] 열거형(enum) (0) | 2022.12.17 |
[자바(Java)] Collections (0) | 2022.12.16 |
[자바(Java)] Set (0) | 2022.12.16 |
[자바(Java)] Comparator와 Comparable (0) | 2022.12.16 |