21장 - 실수를 방지하기 위한 제네릭이라는 것도 있어요
JUnit
- 메서드나 클래스 같은 작은 단위를 쉽게 테스트할 수 있도록 도와주는 프레임워크
DTO (Data Transfer Object) 예시
package d.generic;
import java.io.Serializable;
public class CastingDTO implements Serializable {
private Object object;
public void setObject(Object object) {
this.object = object;
}
public Object getObject() {
return object;
}
}
- private 변수, getter, setter, Serializable 구현해야 제대로 된 DTO 클래스
타입 점검하는 예약어, instanceof
public void checkDTO(CastingDTO dto) {
Object tempObject = dto.getObject();
if(tempObject instanceof StringBuilder) {
System.out.println("StringBuilder");
} else if(tempObject instanceof StringBuffer) {
System.out.println("StringBuffer");
}
}
instanceof를 보완한 제네릭(Generic)
- 타입 형변환에서 발생할 수 있는 문제점 사전에 없애기 위해 만들어짐
- 실행 시 발생하는 예외를 처리하기 위함이 아닌, 컴파일할 때 점검할 수 있도록 함
package d.generic;
import java.io.Serializable;
public class CastingGenericDTO<T> implements Serializable {
private T object;
public void setObject(T object) {
this.object = object;
}
pubic T getObject() {
return object;
}
}
- 여기서의 T
- 가상의 타입 이름이라 생각하면 됨
- 아무런 이름으로 지정해도 컴파일 시 전혀 영향 x
- But 가능한 클래스 이름의 명명 규칙과 동일하게 지정하는 게 좋음
package d.generic;
public class GenericSample {
public static void main(String[] args) {
GenericSample sample = new GenericSampe();
sample.checkGenericDTO();
}
public void checkGenericDTO() {
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();
- 형변환할 필요 x
- 잘못된 타입 입력 시 컴파일 자체가 안 됨 = 실행 시에 다른 타입으로 잘못 형변환해 예외 발생하는 일 x
제네릭 타입 이름 규칙
E | K | N | T | V | S, U, V |
요소(Element) | 키 | 숫자 | 타입 | 값 | 두 번째, 세 번째, 네 번째에 선언된 타입 |
package d.generic;
class WildcardGeneric<W> {
W wildcard;
public void setWildcard(W wildcard) {
this.wildcard = wildcard;
}
public W getWildcard() {
return 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);
}
//String을 사용하는 WildcardGeneric 객체만 받을 수 있음
public void wildcardStringMethod(WildcardGeneric<String> c) {
String value = c.getWildcard();
System.out.println(value);
}
//메서드 내부에서는 WildcardGeneric이 사용하는 값의 타입을 모르기에 Object로 처리해야
//? 로 명시한 타입 : wildcard 타입
public void wildcardStringMethod2(WildcardGeneric<?> c) {
Object value = c.getWildcard();
//만약 넘어오는 타입이 두세 가지로 정해져 있다면 instanceof 예약어로 타입 확인
if(value instanceof String) {
System.out.println(value);
}
}
}
- 제네릭한 클래스의 타입만 바꿈으로 Overloading하는 건 불가능
- wildcard는 메서드의 매개 변수로만 사용하는 게 좋음
public void callWildcardMethod() {
WildcardGeneric<?> wildcard = new WildcardGeneric<String>();
wildcard.setWildcard("A");
wildcardStringMethod(wildcard);
}
- 위와 같이 wildcard로 객체 선언 시, 알수 없는 타입에 String 지정했기에 에러 발생
- 어떤 객체를 wildcard로 선언 후 그 객체의 값은 가져올 수 있음
- But wildcard로 객체를 선언했을 때 특정 타입으로 값 지정은 불가함
- 제네릭 선언에 사용하는 타입의 범위 지정하기
package d.generic;
class Car {
protected String name;
public Car(String name) {
this.name = name;
}
public String toString() {
return "Car name=" + name;
}
}
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"));
boundedWildcardMethod(wildcard);
}
//<? extends Car>
//제네릭 타입으로 Car 상속받은 모든 클래스를 사용할 수 있음
//Car 클래스 상속받은 클래스만 넘어올 수 있음
//만약 그 외의 값 가져오면 컴파일 에러 발생
public void boundedWildcardMethod(WildcardGeneric<? extends Car> c) {
Car value = c.getWildcard();
System.out.println(value);
}
}
- ? extends 타입
- Bounded Wildcards
- 매개 변수로 넘어오는 제네릭 타입의 경계 지정하는데 사용
- 이 타입에는 값 할당 불가함 ➡ 조회용 매개 변수로만 사용해야함
- 메서드 제네릭하게 선언
package d.generic;
public class GenericWildcardSample {
public static void main(String[] args) {
GenericWildcardSample sample = new GenericWildcardSample();
}
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");
}
}
- 메서드의 Bounded Wildcards
public <T extends Car> void boundedGenericMethod(WildcardGeneric<T> c, T addValue)
public <S,T extends Car> void multiGenericMethod(WildcardGeneric<T> c, T addValue, S another)
'Backend > Java' 카테고리의 다른 글
[자바의 신] 23장 (0) | 2022.08.09 |
---|---|
[자바의 신] 22장 (0) | 2022.08.09 |
[자바의 신] 20장 (0) | 2022.07.26 |
[자바의 신] 19장 (0) | 2022.07.26 |
[자바의 신] 18장 (0) | 2022.07.26 |
댓글