제네릭(Generic)이란?

조현태·2023년 3월 22일
0

✨ 제네릭(Generic) ✨

제네릭(Generic)이란?

만약에 우리가 어떤 자료구조를 만들어 공유하려고 하는데 String 타입도 지원하고 싶고 Integer타입도 지원하고 싶고 많은 타입을 지원하고 싶으면 어떻게 해야 할까요??

많은 타입을 지원하기 위해서는 원하는 타입에 따른 String에 대한 클래스, Integer에 대한 클래스 등 하나하나 타입에 따라 만들어야합니다.
하지만 이 방법은 너무 비효율적이고 수정, 관리하기에 어렵기 때문에 이러한 문제를 해결하기 위해 우리는 제네릭이라는 것을 사용합니다.

이렇듯 제네릭(Generic)은 한마디로 특정 타입을 미리 지정해주는 것이 아닌 필요에 의해 지정할 수 있도록 하는 일반 타입이라고 할 수 있습니다.

제네릭의 장점

  • 제네릭을 사용하면 잘못된 타입이 들어올 수 있는 것을 컴파일 단계에서 방지할 수 있습니다.

  • 클래스 외부에서 타입을 지정해주기 때문에 따로 타입을 체크하고 변환해줄 필요가 없습니다.

  • 비슷한 기능을 지원하는 경우 코드의 재사용성이 높아집니다.


✨ 제네릭 메소드 ✨

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


        Integer[] intArray = {1,2,3,4,5};
        Double[] doubleArray = {5.5, 4.4, 3.3, 2.2, 1.1};
        Character[] chrArray = {'H', 'E', 'L', 'L', 'O'};
        String[] stringArray = {"B", "Y", "E"};

        displayArray(intArray);
        displayArray(doubleArray);
        displayArray(chrArray);
        displayArray(stringArray);
    }
}

위의 코드를 실행시키위해서 Overloading을 사용하여 아래와 같은 함수들을 만들어야 합니다.

   public static void displayArray (int[] array) {

        for (Integer x : array) {
            System.out.print(x + " ");
        }
        System.out.println();
    }
    
   public static void displayArray (Double[] array) {

        for (Double x : array) {
            System.out.print(x + " ");
        }
        System.out.println();
    }

    public static void displayArray (Character[] array) {

        for (Character x : array) {
            System.out.print(x + " ");
        }
        System.out.println();
    }

    public static void displayArray (String[] array) {

       for (String x : array) {
            System.out.print(x + " ");
        }
        System.out.println();
   }

하지만 여기서 제네릭을 사용한다면 아래와 같이 코드를 간소화할 수 있습니다.

    public static <Thing> void displayArray (Thing[] array) {

        for (Thing x : array) {
            System.out.print(x + " ");
        }
        System.out.println();
    }

✨ 제네릭 클래스 ✨

public class _02_Generics_Class {
    public static void main(String[] args) {
        MyIntegerClass myInt = new MyIntegerClass(1);
        MyDoubleClass myDouble = new MyDoubleClass(3.14);
        MyCharacterClass myChar = new MyCharacterClass('@');
        MyStringClass myStr = new MyStringClass("Hello");

        System.out.println(myInt.getValue());
        System.out.println(myDouble.getValue());
        System.out.println(myChar.getValue());
        System.out.println(myStr.getValue());
            }
}

위의 코드를 실행하려면 아래와 같은 클래스를 4개를 만들어야합니다.

public class MyIntegerClass <int> {
    int x;

    MyIntegerClass (int x) {
        this.x = x;
    }

    public int getValue () {
        return x;
    }
}

하지만 제네릭을 이용해서 아래와 같이 클래스를 만들면 하나의 클래스로 모든 클래스를 표현할 수 있습니다.

public class MyGenericClass <Thing> {
    Thing x;

    MyGenericClass (Thing x) {
        this.x = x;
    }

    public Thing getValue () {
        return x;
    }
}
public class _02_Generics_Class {
    public static void main(String[] args) {
        // 하나의 클래스로 다양한 자료형을 가진 객체를 만들어낼 수 있다.
        MyGenericClass<Integer> myInt = new MyGenericClass<>(1);
        MyGenericClass<Double> myDouble = new MyGenericClass<>(3.14);
        MyGenericClass<Character> myChar = new MyGenericClass<>('@');
        MyGenericClass<String> myStr = new MyGenericClass<>("Hello");

        System.out.println(myInt.getValue());
        System.out.println(myDouble.getValue());
        System.out.println(myChar.getValue());
        System.out.println(myStr.getValue());
    }
}



✨ 추가 ✨

1. 여러개의 제네릭 인수를 사용할 경우

public class MyGenericClass2 <Thing, Thing2> {
    Thing x;
    Thing2 y;

    MyGenericClass2 (Thing x, Thing2 y) {
        this.x = x;
        this.y = y;
    }

    public Thing2 getValue () {
        return y;
    }
}

위와 같이 Thing, Thing2, ... 처럼 변수를 표현해서 여러 개의 변수들을 사용할 수 있습니다.


2. 바운드 타입 매개변수

그리고 Thing에서도 자료형에 제한을 줄 수 있는데요.

매개변수명, extends, 상위 타입 경계

public class MyGenericClass2 <Thing extends Number, Thing2> {
                  ......
}

위의 코드에서는 Thing변수는 Number에 속하는 Integer, Double만을 사용할 수 있습니다.


하나의 경계가 아니라 여러 개의 경계를 가질 경우, & 기호로 구분합니다.
이때, 타입 매개변수 Thing는 경계로 나열된 모든 타입의 하위 타입입니다.

public class MyGenericClass2 <Thing extends A & B & C, Thing2> {
                  ......
}

3. 와일드 카드

  • 제네릭 : 타입을 모르지만, 타입을 정해지면 그 타입의 특성에 맞게 사용한다.
  • 와일드 카드 : 무슨 타입인지 모르고, 무슨 타입인지 신경쓰지 않는다. 타입을 확정하지 않고 가능성을 열어둔다.

예시 코드)

static void displayArray1(List<?> list) {    

    list.add(list.get(1)); // 컴파일 실패
    
}

와일드 카드는 list에 담긴 원소에는 전혀 관심이 없기 때문에 원소와 관련된 add 메소드를 사용할 수 없어서 컴파일 오류가 발생합니다. (단, null은 들어갈 수 있습니다.)


static <Thing> void displayArray2(List<Thing> list) {     

    list.add(list.get(1)); // 컴파일 성공
    
}

제네릭은 ```list```에 담긴 원소에 관심을 갖기 때문에 원소와 관련된 add 메소드를 사용할 수 있습니다. (당연히 null도 들어갈 수 있습니다.)


출처
https://st-lab.tistory.com/153
https://nauni.tistory.com/143
YouTube : BroCode

0개의 댓글