Java 제네릭(Generics)에 대한 얕은 고찰

fever·2024년 7월 11일

Java 기초

목록 보기
9/10

어느덧 입사한지 어연 1달...
하는 일이라곤 사수들이 차려놓은 밥상에 수저를 얹고
db를 수정한다던가, 기초 crud를 추가하는 일 뿐이라 여러가지 생각이 들었다.

처음엔 왜 이런 걸 시키지? 생각했다가,
더 많은 코드를 보고 나서 생각이 바뀌었다...

부탁입니다. 저에게 시키지마세요. 모르겠어요!

무튼, 매일 같이 공부해도 코드를 볼 때마다 새로운 코린이로서
오늘은 제네릭에 대해 고찰을 해보고자 한다.

제네릭(Generics)이란?

제네릭은 자바에서 타입을 파라미터화 할 수 있는 기능을 제공하며, 특정 데이터 타입에 의존하지 않는 클레스, 인터페이스, 메서드를 작성할 수 있다.

처음 정의를 들었을 땐 ?? 했지만,
코드를 써보고 느껴보니 데이터 형식을 의존하지 않고 쓸 수 있다고 선언하는 개념이다! 즉, 메서드에서 a가 들어온다고 선언하지 않고, a일수도 b일수로 하물며 c일수도... 라며 넓게 받는 방법이다.

무슨 말인지 모르겠다면, 코드를 보자.

//Box라는 클래스는 T라는 타입의 파라미터를 받는다. (임의의 파라미터)
public class Box<T> {
    private T value;

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

    public T get() {
        return value;
    }
}
public class Main {
    public static void main(String[] args) {
        // Integer 타입의 Box
        Box<Integer> integerBox = new Box<>();
        integerBox.set(10);
        Integer intValue = integerBox.get();
        System.out.println(intValue); // 출력: 10

        // String 타입의 Box
        Box<String> stringBox = new Box<>();
        stringBox.set("Hello");
        String strValue = stringBox.get();
        System.out.println(strValue); // 출력: Hello
    }
}

이런식으로! 클래스 내부에서 직접 타입을 지정하는 게 아니라, 외부의 사용자의 의해서 지정되는 것을 의미한다.

즉, 이름 그대로 제네릭(일반)타입이라는 것!

제네릭의 장점
1. 컴파일 타임 타입 체크 가능: 컴파일 타임에서 들어오는 타입을 체크하여 런타임 오류를 줄일 수 있다.
2. 코드 재사용성 증가: 제네릭 클래스를 사용하면 다양한 타입에서 사용 가능하다.
3. 타입 캐스팅의 제거: 타입을 명시적으로 지정할 필요가 없고, 더 간결하고 가독성이 향상된다.

아니, 그래서 실무에서 어떻게 쓴다고??

1. 제네릭 메서드로 타입 변환

즉, 객체를 다른 타입으로 변환 시킬 때 사용이 유용하다.

public class Converter {
    public static <T, U> U convert(T source, Class<U> targetClass) {
        U target = null;
        try {
            target = targetClass.getDeclaredConstructor().newInstance();
            // 필드를 복사하는 로직 추가
        } catch (Exception e) {
            e.printStackTrace();
        }
        return target;
    }
}
public class Main {
    public static void main(String[] args) {
        UserDTO dto = new UserDTO();
        dto.setName("John");
        dto.setAge(30);

        UserEntity entity = Converter.convert(dto, UserEntity.class);
        System.out.println(entity.getName());
        System.out.println(entity.getAge());
    }
}

2.제네릭 인터페이스로 공통 기능 사용

데이터 접근 객체(DAO) 인터페이스를 정의할 때 유용

public interface GenericDAO<T, ID> {
   void save(T entity);
   T findById(ID id);
   void delete(T entity);
}

public class User {
   private String id;
   private String name;
   // getters and setters
}

public class UserDAO implements GenericDAO<User, String> {
   @Override
   public void save(User entity) {
       // 저장 로직
   }

   @Override
   public User findById(String id) {
       // 조회 로직
       return new User();
   }

   @Override
   public void delete(User entity) {
       // 삭제 로직
   }
}

public class Main {
   public static void main(String[] args) {
       UserDAO userDAO = new UserDAO();
       User user = new User();
       user.setId("123");
       user.setName("Alice");

       userDAO.save(user);
       User fetchedUser = userDAO.findById("123");
       System.out.println(fetchedUser.getName());
   }
}

처음 회사에서 제네릭을 만났을 땐 생소하고 당황했지만, 이것저것 알아보면서 조금씩 알게 되었다.

물론, 아직 와일드카드나 공변성에 대해 깊게 이해하진 못했지만 생소했던 문법이 약간 친근하게 느껴진 순간들이라, 기억을 잊지 않고자 블로그에 남겨본다!

+++
보통 많이 사용되는 제네릭 타입표

타입 파라미터설명예시
<T>타입(Type)일반적인 제네릭 클래스 및 메서드
<E>요소(Element)List<E>
<K>키(Key)Map<K, V>
<V>값(Value)Map<K, V>
<N>숫자(Number)Number
<S, U, V>두 번째, 세 번째, 네 번째 타입 파라미터Pair<S, U>, Triple<S, U, V>
profile
선명한 삶을 살기 위하여

0개의 댓글