ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 자바 - 제네릭
    java 2025. 12. 14. 13:10

    1.개요

    제네릭을 처음 접했을때 이해하는데 많이 시간이 소요됐다. 내가 어떻게 이해했는지를 공유하기 위해 해당 문서를 작성하게되었다.

     

    2.제네릭이 필요한 근본적인 이유

     

    Box 클래스가 존재한다.

    package org.src.study;
    
    public class Box {
    
        private String value;
    
        public String getValue() {
            return value;
        }
    
        public void setValue(String value) {
            this.value = value;
        }
    }

     

    Box 에 String 이라는 타입의 값을 담았다.

    package org.src.study;
    
    public class StudyMain {
    
        public static void main(String[] argc){
    
            String StringBox = "스트링박스";
    
            Box box = new Box();
            box.setValue(StringBox);
    
        }
    
    }

     

    하지만 만약 int형, boolean형 등 다른 타입의 값을 담으려면, 해당 타입별로 인스턴스 변수를 가진 클래스를 추가로 생성해야 하는 코드 중복 문제가 발생합니다.

     

    어 잠깐..! 여러 타입을 유연하게 담으려면 자바의 다형성 원리를 활용하여 모든 객체의 조상인 Object 타입을 사용해 클래스를 설계하면 되자나?

    package org.src.study;
    
    public class ObjectBox {
    
        private Object value;
    
        public Object getValue() {
            return value;
        }
    
        public void setValue(Object value) {
            this.value = value;
        }
    
    }

     

    Object 를 사용해서 구현해도된다. 하지만 이는 엄청난 재앙을 부를 수 있다. 왜냐 타입안전성이 보장되지 않기 때문이다.

    아래와 같이 사용하면 전혀 문제가 없어보인다.

    package org.src.study;
    
    public class StudyMain {
    
        public static void main(String[] argc){
    
            String StringBox = "스트링박스";
            ObjectBox box = new ObjectBox();
            box.setValue(StringBox);
    
            int intBox = 30;
            ObjectBox box2 = new ObjectBox();
            box2.setValue(intBox);
    
            boolean booleanBox = true;
            ObjectBox box3 = new ObjectBox();
            box3.setValue(booleanBox);
            
        }
    
    }

     

    하지만 개발은 혼자 하는게 아니다. 여러 개발자가 참여한다.

    package org.src.study;
    
    public class StudyMain {
    
        public static void main(String[] argc){
            
            //A개발자 작성
            String StringBox = "스트링박스";
            ObjectBox box = new ObjectBox();
            box.setValue(StringBox);
            
            //B개발자 작성
            String getStringBox = (String) box.getValue();
            System.out.println(getStringBox);
    
        }
    
    }

     

    A개발자가 StringBox 즉 String 타입 변수를 box 객체에 담아뒀다.

    B개발자가 이를 보고 box 객체의 값을 꺼낼때 String 타입으로 타입변환을 한 후 값을 꺼내서 사용하고 있었다.

    package org.src.study;
    
    public class StudyMain {
    
        public static void main(String[] argc){
    
            //A개발자 작성
            int intBox = 30;
            ObjectBox box = new ObjectBox();
            box.setValue(intBox);
    
            //B개발자 작성
            String getStringBox = (String) box.getValue(); //class java.lang.Integer cannot be cast to class java.lang.String
            System.out.println(getStringBox);
    
        }
    
    }

     

    A개발자가 프로세스 변경으로 인해 box 객체에 int 타입 변수를 넣어버렸다.

    B개발자가 작성한 타입 변환 코드로 인해 런타임에러가 발생했다. (컴파일 에러발생하지않음)

     

    이는 시스템 장애를 유발하는 좋지않은 설계이다. 즉 Object 를 사용하면 다형성으로 인해 여러 타입의 변수를 클래스에서 받을 수 있지만 타입 안전성이 보장되지 않는 엄청난 단점이 있다. 따라서 자바에서는 다형성을 활용하면서 타입안전성까지 보장하는 제네릭이라는걸 만들어놓았다. 이제 제네릭에 대해 한번 알아보겠다.

     

    3.제네릭

    제네릭은 클래스 내부에서 사용하는 타입을 클래스를 정의하는 시점에 결정하는게 아니라 실제 사용하는 객체 생성 시점에 타입을 결정하는 것이다. 이를 통해 제네릭은 타입 안전성이 보장된다.

     

    제네릭을 사용한 Box 클래스의 형태는 아래와 같다.

    package org.src.study;
    
    public class GenericBox <T>{
    
        private T value;
    
        public T getValue() {
            return value;
        }
    
        public void setValue(T value) {
            this.value = value;
        }
    }

     

    그리고 이를 활용해보겠다.

    package org.src.study;
    
    public class StudyMain {
    
        public static void main(String[] argc){
    
            GenericBox<String> box = new GenericBox<>();
    
            //A개발자 작성
            String StringBox = "스트링박스";
            box.setValue(StringBox);
    
            //B개발자 작성
            String getStringBox = box.getValue();
            System.out.println(getStringBox);
    
        }
    
    }

     

    위와 같이 GenericBox 클래스를 정의하는 시점에 클래스 인스턴스 변수의 타입을 특정하지 않고, box 객체를 생성하는 시점에 타입 매개변수를 String으로 구체화하여 인스턴스 변수의 타입을 결정하는 것입니다. 이를 통해 String뿐만 아니라 Integer, Boolean 등 모든 참조 타입을 사용할 수 있게 되어 다형성을 확보할 수 있습니다. 또한, B 개발자가 box 객체를 사용할 때 타입이 이미 확정되어 있으므로 불필요한 형변환을 생략할 수 있습니다.

     

    그리고 아래와 같이 A 개발자가 GenericBox<Integer>로 타입을 변경했을 때, B 개발자가 여전히 String으로 값을 받으려고 하면 컴파일 에러를 발생시켜 줍니다. Object를 사용했을 때와 달리, 런타임 에러가 발생할 일이 없도록 컴파일러가 사전에 오류를 잡아줍니다. 이를 통해 강력한 타입 안전성이 보장됩니다.

    package org.src.study;
    
    public class StudyMain {
    
        public static void main(String[] argc){
    
            GenericBox<Integer> box = new GenericBox<>();
    
            //A개발자 작성
            int intBox = 30;
            box.setValue(intBox);
    
            //B개발자 작성
            String getStringBox = box.getValue();
            System.out.println(getStringBox);
    
        }
    
    }

     

     

    'java' 카테고리의 다른 글

    자바 - 와일드카드  (0) 2025.12.14
    자바 컴파일,런타임과정  (0) 2024.12.21
    스레드  (0) 2024.08.24
    멤버 상수 final , static 알고가기  (0) 2019.12.05
    자바 - 클래스,인스턴스,객체  (0) 2019.12.05
Designed by Tistory.