공부/Java

일단 마시고 본다. (제네릭)

오잎 클로버 2022. 1. 8. 01:08
728x90

자바는 최적화를 시키는 데 도움을 주는 형태인 generic이 있습니다.

 

제네릭은 코드를 최적화 및 일반화시키는 데 있어 굉장한 도움을 줍니다.

자바를 한 지 얼마 되지않으셨거나 혹은 제네릭을 쓰지않은 채로 개발을 하면

중복코드가 굉장히 많이 생길뿐더러 중복 클래스 역시 많이 생깁니다.

(물론 예외처리시에는 중복 클래스를 여러개를 사용하는 경우가 더러 있습니다.)

 

예시를 들어 설명해드리자면

어떠한 형태를 입출력하는 클래스를 만든다고 가정을 해봅시다.

그러면 대표적인 형태인 정수(Integer), 부동소수점수(Float), 실수(Double), 문자열(String) 등 여러가지가 있을 겁니다.

이를 하나하나 클래스로 나타나면 대충 이런 형태일 겁니다.

public class IntegerPrinter {

    private final int index;

    public IntegerPrinter(int index) {
        this.index = index;
    }

    public void print() {
        System.out.println(index);
    }
}
public class FloatPrinter {

    private final float index;

    public FloatPrinter(float index) {
        this.index = index;
    }

    public void print() {
        System.out.println(index);
    }
}
public class DoublePrinter {

    private final double index;

    public DoublePrinter(double index) {
        this.index = index;
    }

    public void print() {
        System.out.println(index);
    }
}
public class StringPrinter {

    private final String index;

    public StringPrinter(String index) {
        this.index = index;
    }

    public void print() {
        System.out.println(index);
    }
}

이러면 비효율적이니, 인터페이스를 사용하는 것 역시 생각해볼 수 있겠습니다.

 

인터페이스를 사용하면 아래와 같습니다. (IntegerPrinter만 올렸습니다.)

public interface IPrinter {

    default void print(Object index) {
        if (index instanceof Integer integer) {
            System.out.println(integer);
        }
        else if (index instanceof Float afloat) {
            System.out.println(afloat);
        }
        else if (index instanceof Double aDouble) {
            System.out.println(aDouble);
        }
        else if (index instanceof String s) {
            System.out.println(s);
        }
    }

}
public class IntegerPrinter implements IPrinter{

    private final int index;

    public IntegerPrinter(int index) {
        this.index = index;
    }

    public int getIndex() {
        return index;
    }
}

 

그런데 이렇게 하더라도 비효율적이라는 것은 어김없이 동일할 것입니다.

그래서 제네릭을 사용합니다.

제네릭(generic)이란?
데이터의 타입(data type)을 일반화(generalize)한다는 것을 의미합니다.
클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법입니다.

즉 이런 복잡하고 개판오분전인 코드를 제네릭을 활용하여 일반화시켜봅시다.

public class Printer<T> {

    private final T value;

    public Printer(T value) {
        this.value = value;
    }

    public void print() {
        System.out.println(value);
    }
}

참 간단하고 단순화가 이루어졌습니다.

 

코드에 대해서 설명을 약간 드리자면

클래스 옆에 있는 저 <T>은 제네릭 클래스임을 말해줍니다.

그렇다고 굳이 T를 사용하실 필요는 없지만, 약간 암묵적인 룰처럼 

T는 Type을

K는 Key를

V는 Value를 

과 같이 어느정도 암묵적인 룰처럼 이어져온 것들이 있습니다.

물론 T보다는 Type이라고 끝까지 적어도 전혀문제가 발생하지 않습니다.

(단지 적어야할 것이 더 많아진다는 점...그리고 협업할 때 서로가 헷갈려질 수가 있다는 점이 치명적이겠지요.)

 

public class Main {
    public static void main(String[] args) {
        Printer<Integer> integerPrinter = new Printer<>(10);
        integerPrinter.print();
    }
}

한 줄 코딩

public class Main {
    public static void main(String[] args) {
        new Printer<>(50).print();
    }
}

 

이상입니다.