제네릭
import java.io.Serializable;
public class CastingDTO implements Serializable{
//타입이 Object이므로 어떤 타입이든 사용할 수 있다.
priavet Object object;
public void setObject(Object object){
this.object=object;
}
public Object getObject(){
return object;
}
}
위 경우 Object타입을 인자로 받으므로 생성시 String이나 StringBuffer등 여러타입으로 생성할 경우 각각 타입으로 형 변환을 해야한다. 또한 여러 타입으로 생성시 각 객체가 어떤 타입인지 혼동 될 수 있다.
따라서
import java.io.Serializable;
public class CastingGenericDTO<T> implements Serializable{
priavet T object;
public void setObject(T object){
this.object=object;
}
public T getObject(){
return object;
}
}
T는 아무런 이름을 지정하여도된다.
<>안에는 현재 존재하는 클래스를 사용해되 되고, 존재하지 않는 것을 사용해도 된다.
되도록이면 클래스 이름의 명명 규칙과 동일하게 지정하는 것이 좋다.
선언 후 클래스 안에서 하나의 타입 이름처럼 사용하면된다.
만약 <>안의 이름이 현재 존재하는 클래스여야한다면, 컴파일시 에러가 발생할 것이다.
따라서, 가상 타입의 이름이라고 생각하면 된다.
제네릭을 사용하지 않을 경우 예시를 보면,
public class GenericSample{
public static void main(String[] args){
GenericSample sample = enw GenericSample();
sample.checkCastingDTO();
}
public void checkCastingDTO(){
CastingDTO dto1=new CastingDTO();
dto1.setObject(new String());
CastingDTO dto2=new CastingDTO();
dto2.setObejct(new StringBuffer());
CastingDTO dto3=new CAstingDTO();
dto3.setObject(new StringBuilder());
}
}
String temp1 = (String)dto1.getObject();
StringBuffer temp2 = (StringBuffer)dto2.getObject();
StringBuilder temp3 = (StringBuilder)dto3.getObject();
리턴 값으로 넘어는 타입은 Object이므로 형변환을 해주어야한다.
dto2의 인스턴스 변수 타입이 StringBuilder인지 StringBuffer인지 혼동된다면??
instanceof를 사용하여서 점검해도 되지만,
CastingGenericDTO클래스를 이용하여
public void checkGenericDTO(){
CastingGenericDTO<String> dto1 = new CastingGenericDTO<String>();
dto1.setObject(new String());
CastingGenericDTO<StringBuffer> dto2 = new CasintgGenericDTO<StringBuffer>();
dto2.setObject(new StringBuffer());
CastingGenericDTO<StringBuilder> dto3 = new CasintgGenericDTO<StringBuilder>();
dto3.setObject(new StringBuilder());
}
String temp1 = dto1.getObject();
StringBuffer temp2 = dto2.getObject();
StringBuilder temp3 = dto3.getObject();
로 사용할 수 있다.
<>안에 타입을 명시적으로 적어주게되어 헷갈리지 않는다.
실행시에 타른 타입으로 잘못 형변환되어 예외가 발생하는 일이 없어진다
또한 형변환을 해주지 않아도 된다.
결과적으로 실행시 다른 타입으로 잘 못 형변환한하여 예외가 발생하는 일이 없어진다.
제네릭한 클래스의 타입만 바꾼다고 Overriding이 불가능하다.
public class WildcardGeneric<W>{
W wildcard;
public void setWildcard(W wildcard){
this.wildcard = wildcard;
}
pubilc W getWildcard(){
return wildcard;
}
}
set, get하는 간단한 class이다
public class WildcardSample{
public static void main(String[] args){
WhildcardSample sample = new WildcardSample();
sample.callWildcardMethod();
}
public void callWildMethod(){
//String을 사용하는 제네릭한 객체를 생성함
WildcardGeneric<String> wildcard = new WildcardGeneric<String>();
wildcard.setWildcard("A");
wildcardMethod(wildcard);
}
public void wildcardMethod(wildcardGeneric<String> c){
String value = c.getwildcard();
System.out.println(value);
}
}
만약 <String>이 아니라 WildcardGeneric<Integer>과 같이 선언된 객체를 받으려면??
불가능하다.
제네릭한 클래스의 타입만 바꾼다고 오버라이딩이 가능하진 않다.
public void wildcardMethod(WhildcardGeneric<?> c){
//<- ?로 적어주면 어떤 제너릭 타입이 되더라도 상관없다.
Object value=c.getWildcard(); //정확한 타입을 모르므로, Object로 값을 받아야한다.
System.out.println(value);
}
이렇게는 사용 불가하다.
public void callWildMethod(){
//알수 없는 타입에 String을 지정할 수 없다고 에러가 난다.
WildcardGeneric<?> wildcard = new WildcardGeneric<String>();
wildcard.setWildcard("A");
wildcardMethod(wildcard);
}
객체에 제너릭 타입으로 값을 지정하는 것은 불가능하다.
제네릭 선언에 사용하는 타입의 범위도 지정할 수 있다.
<? extends 타입>으로ㅓ 선택한다.
//Car
public class Car{
protected String name;
public Car(String name){
this.name=name;
}
public String toString(){
return "Car name="+name;
}
}
//Bus
public class Bus extends Car{
public Bus(String name){
super(name);
}
public String toString(){
return "Bus name="+name;
}
}
//WildcardSample
public class WildcardSample{
public static void main(String[] args){
WhildcardSample sample = new WildcardSample();
sample.callWildcardMethod();
}
public void callWildMethod(){
//String을 사용하는 제네릭한 객체를 생성함
WildcardGeneric<String> wildcard = new WildcardGeneric<String>();
wildcard.setWildcard("A");
wildcardMethod(wildcard);
}
public void wildcardMethod(wildcardGeneric<String> c){
String value = c.getwildcard();
System.out.println(value);
}
public void callBoundedWildcardMethod(){
//Car을 사용하는 제네릭 객체 생성
WildcardGeneric<Car> wildcard = new WildcardGeneric<Car>();
wildcard.setWildcard(new Car("BMW"));
wildcardMethod(wildcard);
}
//?는 어떠한 타입이 와도 상관이 없다.
//제네릭 타입으로 Car를 상속받은 모든 클래스를 사용할 수 있다는 의미가 된다.
//즉 반드시 Car클래스를 확장한 클래스가 넘어와야한다.
public void boundedWildcardMethod(WildcardGeneric <? extends Car> c){
Car value=c.getWildcard();
System.out.println(value);
}
}
public void callBoundedWildcardMethod(){
//Bus을 사용하는 제네릭 객체 생성
WildcardGeneric<Bus> wildcard = new WildcardGeneric<Bus>();
wildcard.setWildcard(new Bus("Bus"));
wildcardMethod(wildcard);
}
callBoundedWildcardMethod()를 호출하면, Bus를 사용하는 제네릭한 객체를 넘겨주어도 실행이 잘 된다.
Bus클래스는 Car클래스를 상속받기 때문이다.
public class WildcardGeneric<W>{
W wildcard;
public void setWildcard(W wildcard){
this.wildcard = wildcard;
}
pubilc W getWildcard(){
return wildcard;
}
}
//wildcardSample
...
public <T> void genericMethod(WildcardGeneric<T> c, T addValue){
c.setWildcard(addValue);//값을 할당함
T value = c.getWildcard();
System.out.println(value);
}
//genericMethod 사용
public void callGenericMethod(){
WildcardGeneric<String> wildcard = new WildcardGeneric<String>();
genericMethod(wildcard, "Data");//string을 사용하는 제네릭한 객체와 T타입의 변수
}
public <S, T extends Car > void genericMethod(WildcardGeneric<T> c, T addValue, S another){
...
}
//도 가능하다.
//S와 T라는 제네릭 타입을 메소드에서 사용할 수 있다.
리턴타입 앞 로 제네릭 타입을 선언했다.
매개변수에는 제네릭타입이 포함된 객체를 받아서 처리했다.
메소드 선언시 리턴타입 앞에 제네릭한 타입을 선언해주고 그 타입을 매개 변수에서 사용하면 컴파일할 때 문제가 없다.
'공부 > Spring' 카테고리의 다른 글
스프링 웹개발 기초 (0) | 2021.10.20 |
---|---|
자바 - Map (0) | 2021.10.19 |
자바 - Set (0) | 2021.10.18 |
자바 - 컬렉션(List) (0) | 2021.10.15 |
자바 문법 (0) | 2021.10.13 |