JAVA

[Java] 제네릭

songsua 2025. 2. 9. 23:15
package genegic;
//슷자를 하나 넣고 뺄 수 있는 값
public class IntegerBox {
    private Integer value;

    public void set(Integer value) {
        this.value = value;
    }

    public Integer get() {
        return value;
    }
}

set 메서드로 value값을 받아온다.

package genegic;

public class BoxMain {
    public static void main(String[] args) {
        IntegerBox integerBox = new IntegerBox();
        integerBox.set(10); //오토박싱
        Integer integer = integerBox.get();
        System.out.println(integer);



        StringBox stringBox = new StringBox();
        stringBox.setvalue("Hello");
        String str = stringBox.get();
        System.out.println(stringBox.get());
        System.out.println(str);
    }
}
10
Hello
Hello

 

이렇게 사용하다가, 다음에 Boolean, Double 타입의 박스가 필요하면 각각의 타입별로 이 클래스들을 새로 만들어야 한다.

이 문제를 어떻게 해결할까?

 

다형성을 통한 중복 해결 시도

Object는 모든 타입의 부모이다. 따라서 다형성을 사용하여 문제를 해결할 수 있다.

package genegic;

public class ObjectBox {
    private Object value;

    public void set(Object object) {
        this.value = object;
    }

    public Object get() {
        return value;
    }
}

 

package genegic;

public class BoxMain {
    public static void main(String[] args) {
        //ObjectBox사용해서 하기

        ObjectBox objectBox = new ObjectBox();
        objectBox.set(10);
        Integer integer = (Integer) objectBox.get();
        System.out.println(integer);
    }
}

Object 에서 String 으로 변환하여 사용하는 것 

 

**잘못된 타입의 인수전달 문제 **

set(Object)메서드는 모든 타입의 부모인 Object를 매개변수로 받기 때문에 어떤 데이터도 입력받을 수 있다. 따라서 이렇게 원하던 문자열을 입력해도 문제가 되지 않는다.

하지만, 잘못된 타입의 값을 전달하면 값을 꺼낼 때 문제가 발생한다.

integer.ser("Hello");

Integer result = (Integer) integerBox.get();
Integer result = (Integer) "Hello";
Integer result = (Integer) "Hello";  //예외 발생 String을 Integer로 캐스팅할 수 없다.

숫자를 기대했는데 실수로 문자열을 넣어서 원하지 않는 타입을 반환받을 수 없게 된다.

따라서 이러한 방식은 타입 안전성이 떨어진다.

 

** 반환 타입이 맞지 않는 문제 **

하지만 이렇게 하면 문제가 있는게, int값을 넣어야하는 것을 String 값으로 넣는 것이다.

Object.Box.set("안녕하세요") 같이 넣었을 때는 컴파일 오류가 발생할 수 있다.

Object는 부모이기 때문에 Object 는 Integer 타입으로 직접 다운 캐스팅해야 한다. String 도 마찬가지이다.

Object obj =integerBox.get();
//Integer안에 Object를 넣는 것은 불가능하다

 

따라서, (Integer)타입 캐스팅 코드를 넣어서 Object 타입을 Integer 타입으로 직접 다운 캐스팅해야한다.

Integer integer = (Integer) integerBox.get()
//get 하여 object value 가 나온다
Integer integer = (Integer) (Object)value
Integer integer = (Integer) value 

//다운캐스팅하여 Object타입을 Integer로 다운캐스팅한다

 

    이러한 두 문제점을 해결하기 위해서 제네릭을 사용한다.

 

제네릭 사용

<>를 사용한 클래스를 제네릭 클래스라고 한다. <> 기호를 보통 다이아몬드라고 한다.

제네릭 클래스를 사용할 때는 Integer,String 같은 타입을 미리 결정하지 않는다.

대신에 클래스명 오른쪽에 <T>와 같이 선언하면 제네릭 클래스가 된다.

여기서 T를 타입 매개변수라고 한다. 이 타입 매개변수는 이후에  Integer, String 으로 변할 수 있다.

그리고 클래스 내부에 T 타입이 필요한 곳에 T value와 같이 타입 매개변수를 적어두면 된다. 

package genegic.ex1;

public class GenericBox<T> {
    private T value;
    
    public void set(T value) {
        this.value = value;
    }
    
    public T get() {
        return value;
    }
}


꼭 T를 사용하지 않아도 된다.

package genegic.ex1;

public class GenericMain {
    public static void main(String[] args) {
        
        GenericBox<Integer> integerBox = new GenericBox<Integer>();  //<> 통해 T의 타입 결정
        integerBox.set(10);
        
        //Integer 타입만 허용, 컴파일 오류
        integerBox.set("안녕하세요");


    }
}


이전에 하나하나 객체 만들어서 오토박싱했을 때랑 비교해보면

package genegic;

public class BoxMain {
    public static void main(String[] args) {
        IntegerBox integerBox = new IntegerBox();
        integerBox.set(10); //오토박싱
        Integer integer = integerBox.get();
        System.out.println(integer);



        StringBox stringBox = new StringBox();
        stringBox.setvalue("Hello");
        String str = stringBox.get();
        System.out.println(stringBox.get());
        System.out.println(str);
    }
}

코드가 좀 줄어든걸로 보인다

package genegic.ex1;

public class GenericMain {
    public static void main(String[] args) {

        GenericBox<Integer> integerBox = new GenericBox<Integer>();  //<> 통해 T의 타입 결정
        integerBox.set(10);

        //Integer 타입만 허용, 컴파일 오류
//        integerBox.set("안녕하세요");

        //원하는 모든 타입 사용 가능
        GenericBox<Double> doubleBox = new GenericBox<Double>();
        doubleBox.set(3.14);
        Double d = doubleBox.get();
        System.out.println(d);


    }
}

 

두번 적는걸을 생략하는 것도 가능하다 //이름 타입 추론이라고 한다

GenericBox<Double> doubleBox = new GenericBox<>();

당연하게 double일 것이라고 생각하여 뒤에는 생략해도 된다.

 

이로 인해 코드 재사용과 타입 안정성이라는 두 이점을 잡을 수 있다.

 

 

제네릭 활용 사례

public class ResultResponse<T> {
 private boolean success;
 private String code;
 private String message;
 private T data;
 public static <T> ResultResponse<T> ok(T data) { return new ResultResponse<>(true, “200”, null, data); }
 public static <T> ResultResponse<T> fail(String message) {
 return new ResultResponse<>(false, “500”, message, null);
 }
 }
 public ResultResponse<AbcDto> search() {
 return ResultRespon

 

  • ResultResponse<T>: 제네릭 클래스로, T라는 타입을 사용합니다.
    T는 실제 사용될 때 지정되며, 다양한 데이터 타입을 저장할 수 있습니다.
  • success: 요청이 성공했는지 여부 (true 또는 false)
  • code: 상태 코드 (200, 500 등)
  • message: 오류 메시지 등을 저장할 문자열
  • data: 응답 데이터 (T 타입)

1. 응답을 만드는 메서드

public static <T> ResultResponse<T> ok(T data) {
    return new ResultResponse<>(true, "200", null, data);
}

 

 

  • 성공(success = true)한 응답을 생성합니다.
  • HTTP 응답 코드 "200" (성공)을 설정합니다.
  • data(응답 데이터)를 받아서 저장합니다.
public static <T> ResultResponse<T> fail(String message) {
    return new ResultResponse<>(false, "500", message, null);
}

 

 

  • 실패(success = false)한 응답을 생성합니다.
  • HTTP 응답 코드 "500" (서버 오류)을 설정합니다.
  • 실패 원인을 설명하는 message를 저장하고, data는 null로 설정합니다.

 

제네릭을 사용하지 않을 경우에는 어떻게 구성하게 될까?

public class AbcResponse {
    private boolean success;
    private String code;
    private String message;
    private AbcDto data;
}
public class XyzResponse {
    private boolean success;
    private String code;
    private String message;
    private XyzDto data;
}

 

 

⚠️ 문제점

  • 데이터 타입이 달라질 때마다 클래스를 새로 만들어야 해서 중복 코드가 많아짐
  • 관리가 어렵고 유지보수가 힘듦

제네릭을 사용하면

  • T 타입을 활용하여 하나의 클래스로 여러 데이터를 처리할 수 있음
  • 코드가 짧고 재사용 가능

'JAVA' 카테고리의 다른 글

[Java] 지역 클래스  (0) 2025.02.15
[Java] Thread  (2) 2025.02.11
[Java] 중첩 클래스, 내부 클래스  (0) 2025.02.09
[Java] 문자열과 타입 안전성  (0) 2025.02.08
[Java] 다시 기본 복습(1)  (0) 2025.01.26