본문 바로가기

백엔드 기술/Java

Java - Generic 제네릭이란 ?

제네릭(Generic)

Java에서 Generic은, 클래스나 메서드에서 사용할 데이터 타입을 내부가 아닌 외부에서 지정하는 방법이다.
즉, 데이터 타입을 미리 지정해주는 대신 타입의 경계만 지정하고, #컴파일 때 필요한 타입으로 캐스팅하는것

[장점]

1. 제네릭을 사용하면 잘못된 타입이 들어올 수 있는 것을 #컴파일 단계에서 방지할 수 있다.
2. 클래스 외부에서 타입을 지정해, 내부에서 따로 타입을 체크하고 변환해줄 필요가 없다. = 관리가 편함
3. 비슷한 기능을 지원하는 경우, 타입이 정해지지 않았으므로 코드의 재사용성이 높아진다.

[사용 방법]

타입 설명
<T> Type
<E> Element
<K> Key
<V> Value
<N> Number

정해진 규칙은 아니지만, 일반적으로 위와 같이 사용하고 있다.

 

#1. 클래스 및 인터페이스 사용

public class ClassName <T> { ... }

위와 같이 클래스명 옆에 <>를 사용해 제네릭을 적용할 수 있다.

이 때 주의할 점은 타입 파라미터로 지정하는 것은 #참조 타입 Only 지정할 수 있다.
즉, #기본형 타입은 올 수 없는데 만일 int, dobule과 같은 #기본형이 필요할 땐
Integer, Double과 같은 #Wrapper Type으로 지정해서 사용한다. 
#참조타입이 올 수 있다는 것은 사용자가 정의한 #커스텀 클래스 타입 또한 타입 파라미터로 지정할 수 있다.

public class ClassName <T> { ... }
 
public class Student { ... }
 
public class Main {
	public static void main(String[] args) {
		ClassName<Student> a = new ClassName<Student>();
	}
}

 

<제네릭 클래스 및 객체 사용 예제>

class ClassName<E>{
    private E element;

    public E getElement() {
        return element;
    }

    public void setElement(E element) {
        this.element = element;
    }
}

public class Main {
    public static void main(String[] args) {
        ClassName<String> a = new ClassName<>();
        ClassName<Integer> b = new ClassName<>();

        a.setElement("10");
        b.setElement(10);

        System.out.println("a data: "+a.getElement());
        System.out.println("b data: "+b.getElement());

        System.out.println("a의 클래스타입: "+a.getElement().getClass().getName());
        System.out.println("b의 클래스타입: "+b.getElement().getClass().getName());
    }
}

위의 예제 실행 결과

이렇게 외부 클래스(main)에서 제네릭 클래스를 생성할 때
<> 안에 원하는 타입을 파라미터로 전달하여 생성하는 것이
Java의 #제네릭 프로그래밍이다.

 

#2. 제네릭 메서드 및 사용 예제

[접근제어자] <제네릭> [반환타입] [메서드명] ( [제네릭타입] [파라미터] ) {}

    public <T> T genericMethod(T element) {
        return element;
    }
class ClassName<E>{
    private E element;

    public E getElement() {
        return element;
    }

    public void setElement(E element) {
        this.element = element;
    }

    public <T> T genericMethod(T element) {
        return element;
    }
}

public class Main {
    public static void main(String[] args) {
        ClassName<String> a = new ClassName<>();
        ClassName<Integer> b = new ClassName<>();

        a.setElement("10");
        b.setElement(10);

        System.out.println("a data: "+a.getElement());
        System.out.println("b data: "+b.getElement());

        System.out.println("a의 클래스타입: "+a.getElement().getClass().getName());
        System.out.println("b의 클래스타입: "+b.getElement().getClass().getName());

        System.out.println("<T> 메서드 return 타입 :"+a.genericMethod(3).getClass().getName());
        System.out.println("<T> 메서드 return 타입 :"+a.genericMethod("hey").getClass().getName());
        System.out.println("<T> 메서드 return 타입 :"+a.genericMethod(a).getClass().getName());
    }
}

genericMethod()는 파라미터 타입에 따라 그 타입이 결정되도록 되어 있다.
위의 예제 실행 결과

즉, 클래스에서 지정한 제네릭 유형과 달리 메서드에서 독립적으로 제네릭 유형을 선택해 사용할 수 있다.

위와 같은 방식이 필요한 이유는 '정적(static) 메서드로 선언할 때' 필요하기 때문이다.
static 메서드는 객체가 생성되기 전에 이미 메모리에 올라가 있고, 클래스명.메서드 명으로 바로 사용 가능.

class ClassName<E>{
    private E element;

    public E getElement() {
        return element;
    }

    public void setElement(E element) {
        this.element = element;
    }

    public <T> T genericMethod(T element) {
        return element;
    }

    public static <X> X exampleMethod(X example) {
        return example;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println(ClassName.exampleMethod("hi"));
        System.out.println("Method Type: "+ClassName.exampleMethod("I am IronMan").getClass().getName());
    }
}

위의 예제 실행 결과.

이렇게 제네릭 클래스의 객체가 생성되기 전 제네릭 클래스와 별도로 제네릭 메서드를 사용할 수 있다.

[결론]

제네릭이란 데이터의 타입을 내부에서 미리 정하는게 아니라,
외부에서 필요에 따라 거기에 맞게끔 캐스팅 할 수 있도록 하는 것.

[장점]

1. 제네릭을 사용하면 잘못된 타입이 들어올 수 있는 것을 #컴파일 단계에서 방지할 수 있다.
2. 클래스 외부에서 타입을 지정해, 내부에서 따로 타입을 체크하고 변환해줄 필요가 없다. = 관리가 편함
3. 비슷한 기능을 지원하는 경우, 타입이 정해지지 않았으므로 코드의 재사용성이 높아진다.

'백엔드 기술 > Java' 카테고리의 다른 글

SingleTon 디자인 패턴  (0) 2023.04.20
컴파일(Compile)과 런타임(Runtime)  (0) 2023.04.20
컬렉션과 스트림의 차이  (0) 2023.04.19
자료 구조에서 Array VS List  (0) 2023.04.18
List, Set, Map 의 차이점  (0) 2023.04.18