자바 - 제네릭

공부/Spring 2021. 10. 14. 18:33
반응형

제네릭

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라는 제네릭 타입을 메소드에서 사용할 수 있다.

 

리턴타입 앞 로 제네릭 타입을 선언했다.

매개변수에는 제네릭타입이 포함된 객체를 받아서 처리했다.

메소드 선언시 리턴타입 앞에 제네릭한 타입을 선언해주고 그 타입을 매개 변수에서 사용하면 컴파일할 때 문제가 없다.

 

728x90
반응형

'공부 > Spring' 카테고리의 다른 글

스프링 웹개발 기초  (0) 2021.10.20
자바 - Map  (0) 2021.10.19
자바 - Set  (0) 2021.10.18
자바 - 컬렉션(List)  (0) 2021.10.15
자바 문법  (0) 2021.10.13
블로그 이미지

아상관없어

,