본문 바로가기
Backend/Java

[자바의 신] 21장

by unknownomad 2022. 8. 9.

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

댓글