21장 실수를 방지하기 위한 제네릭이라는 것도 있어요
- 제네릭이 자바에 추가된 이유는?
- 타입 형변환에서 발생할 수 있는 문제를 방지하기 위해서
- 제네릭 타입의 이름은 T나 E처럼 하나의 캐릭터로 선언해야 하는가?
- x
- 메소드에서 제네릭 타입을 명시적으로 지정하기 애매할 경우에는 <> 안에 어떤 기호를 넣어 주어야 하는가?
- ?
- 메소드에서 제네릭 타입을 명시적으로 지정하기에는 애매하지만 어떤 클래스의 상속을 받은 특정 타입만 가능하다는 것을 나타내려면 <> 안에 어떤 기호를 넣어야 하는가?
- ? extends 타입
- 제네릭 선언시 wildcard라는 것을 선언했을 때 어떤 제약사항이 있는지?
- 매개변수에 사용해서 값을 얻을 때는 상관없지만, wildcard로 객체를 생성하고, 특정 타입을 세팅할 수는 없다
- 메소드를 제네릭하게 선언하려면 리턴 타입 앞에 어떤것을 추가해주면 되는지?
- 원하는 제네릭 타입을 명시 ex) <T>, <Car>
제네릭이란?
- 형 변환시 발생할 수 있는 문제를 사전에 방지하기 위해 만들어졌다. 여기서 "사전"이라고 함은, 실행시에 예외가 발생하는 것을 처리하는 것이 아니라 컴파일할 때 점검할 수 있도록 한 것을 의미한다.
- 명시적으로 타입을 지정할 때 사용하는 것
public void checkCastingDTO() {
CastingDTO dto1 = new CastingDTO();
dto1.setObject(new String());
String temp1 = (String) dto1.getObject();
CastingDTO dto2 = new CastingDTO();
dto2.setObject(new StringBuffer());
StringBuffer temp2 = (StringBuffer) dto2.getObject();
System.out.println(temp2);
CastingDTO dto3 = new CastingDTO();
dto3.setObject(new StringBuilder());
StringBuilder temp3 = (StringBuilder) dto3.getObject();
System.out.println(temp3);
}
문제 : 제네릭 사용전에는, 컴파일과 문제없이 실행된다 => Object 클래스는 모든 클래스의 부모 클래스 이므로 어떤 참조 자료형을 넘겨도 상관 없기 때문. 그러나 값을 꺼낼 때의 타입은 Object이므로, 각 타입에 알맞게 캐스팅을 해줬어야 했음
public void checkCastingDTO() {
CastingGenericDTO<String> dto1 = new CastingGenericDTO<String>();
dto1.setObject(new String());
CastingGenericDTO<StringBuffer> dto2 = new CastingGenericDTO<StringBuffer>();
dto2.setObject(new StringBuffer());
CastingGenericDTO<StringBuilder> dto3 = new CastingGenericDTO<StringBuilder>();
dto3.setObject(new StringBuilder());
String temp1 = dto1.getObject();
StringBuffer temp2 = dto2.getObject();
StringBuilder temp3 = dto3.getObject();
}
해결 : 제네릭을 사용하여, 타입을 명시해주면 값을 가져올 때 캐스팅 할 필요 없음
<?> 이란
- 제네릭 사용시, <> 안에 들어가는 타입은 기본적으로 어떤 타입이라도 상관 없다.
- ?로 명시한 타입을 영어로는 wildcard 타입이라고 한다
public class WildcardSample{
public static void main(String[] args){
WildcardSample sample = new WildcardSample();
sample.callWildcardMethod();
}
public void callWildcardMethod(){
WildcardGeneric<String> wildcard = new WildcardGeneric<String>();
wildcard.setWildcard("A");
wildcardStringMethod(wildcard);
}
public void wildcardStringMethod(WildcardGeneric<String> c){
Object value = c.getWildcard();
System.out.println(value);
}
}
문제 : 이 메소드의 매개 변수는 반드시 String을 사용하는 WildcardGeneric객체만 받을 수 있다
public void wildcardStringMethod(WildcardGeneric<?> c){
Object value = c.getWildcard();
System.out.println(value);
}
해결 : String 대신 ?을 사용하면 어떤 타입이 제네릭 타입이 되더라도 상관 없다. 하지만 메소드 내부에서는 해당 타입을 정확히 모르기 때문에 String으로 값을 받을 수 없고, Object로 처리해야 한다.
public void callWildcardMethod(){
WildcardGeneric<?> wildcard = new WildcardGeneric<String>();
wildcard.setWildcard("A");
wildcardStringMethod(wildcard);
}
주의 : wildcard는 메소드의 매개 변수로만 사용하는 것이 좋다. 왜냐하면, 알 수 없는 타입에 String을 지정할 수 없기 때문이다.
즉, 어떤 객체를 wildcard로 선언하고, 그 객체의 값을 가져올 수는 있지만, wildcard 객체를 선언했을 때는 특정 타입으로 값을 지정하는 것은 불가능하다.
제네릭 선언에 사용하는 타입의 범위 지정
- wildcard로 사용하는 타입을 제한할 수 있다
- ? extends 타입
- Bounded Wildcards라고 부른다
public class Car {
protected String name;
public Car(String name){
this.name = name;
}
public String toString(){
return "Car name="+name;
}
}
public class Bus extends Car{
public Bus(String name) {
super(name);
}
public String toString(){
return "Bus name="+name;
}
}
public class CarWildcardSample {
public static void main(String[] args){
CarWildcardSample sample = new CarWildcardSample();
sample.callBoundedWildcardMethod();
}
public void callBoundedWildcardMethod(){
WildcardGeneric<Car> wildcard = new WildcardGeneric<Car>();
//wildcard.setWildcard(new Car("Mustang"));
wildcard.setWildcard(new Car("980"));
boundedWildcardMethod(wildcard);
}
public void boundedWildcardMethod(WildcardGeneric<? extends Car> c){
Car value = c.getWildcard();
System.out.println(value);
}
결과
// Car name=Mustang
Bus name=980
- ? extends Car이라고 함은, 제네릭 타입으로 Car를 상속받은 모든 클래스를 사용할 수 있다는 의미.
따라서, boundWildcardMethod()의 매개 변수에는 다른 타입을 제네릭 타입으로 선언한 객체가 넘어올 수 없다. - 즉, 컴파일시에 에러가 발생하므로 반드시 Car 클래스와 관련되어 있는 상속한 클래스가 넘어와야만 한다. (위에서는 string)
메소드를 제네릭하게 선언하기
- 메소드 선언시 리턴 타입 앞에 제네릭한 타입을 선언해주고, 그 타입을 매개 변수에서 사용하면 컴파일 문제가 없다. 값 할당도 가능
public class GenericWildcardSample {
public static void main(String[] args){
GenericWildcardSample sample = new GenericWildcardSample();
sample.callGenericMethod();
}
public <T> void genericMethod(WildcardGeneric<T> c, T addValue){
c.setWildcard(addValue);
T value = c.getWildcard();
System.out.println(value);
}
public void callGenericMethod(){
WildcardGeneric<String> wildcard = new WildcardGeneric<String>();
genericMethod(wildcard, "Data");
}
}
'JAVA > about java' 카테고리의 다른 글
[Collection] Set, Queue (0) | 2023.04.30 |
---|---|
[Collection] List (0) | 2023.04.30 |
[GC] Garbage Collector (0) | 2023.04.29 |
[Thread safe] Thread safe / unsafe 기준과 코드 설계 방법 (0) | 2023.04.25 |
[JVM] jvm / java.lang (1) | 2023.04.25 |