상세 컨텐츠

본문 제목

[JAVA] 제네릭(Generic)

JAVA

by jeonghojin 2023. 5. 1. 08:13

본문

제네릭(Geniric)

  • 데이터의 타입을 일반화 한다는 것을 의미.
  • 클래스나 메소드에서 사용할 내부 데이터를 컴파일 시에 미리 지정하는 방법.
  • 클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법

* 특정 타입을 미리 지정해주는 것이 아닌 필요에 의해 지정할 수 있도록 하는 일반 타입이다.

* 정확히 말하면 지정된다는 것보다 타입의 경계를 지정하고, 컴파일 때 해당 타입으로 캐스팅하여 매개변수화 된 유형을 삭제하는 것이다.

장점

  • 비제네릭 타입의 코드에서 발생하는 불필요한 타입 변환으로 인한 프로그램 성능의 저하를 감소.
  • 클래스나 메소드 내부에서 사용되는 객체의 타입 안정성을 높일 수 있다.
  • 반환값에 대한 타입 변환 및 타입 검사에 들어가는 노력을 줄일 수 있다.
  • 비슷한 기능을 지원하는 경우 코드의 재사용성이 높아진다.

 

사용법

타입 설명
<T> Type
<E> Element
<K> Key
<V> Value
<N> Number
  • 반드시 한 글자일 필요 없음
  • 위와 반드시 일치할 필요 없음.
  • 대중적으로 통하는 통상적인 선언이므로 암묵적 규칙이라 생각하자.

클래스 및 인터페이스 선언

 

  • 클래스나 인터페이스의 경우
public class ClassName <T> {}

public interface InterfaceName<T> {}

 

제네릭 타입을 두개로 두는 경우 (HashMap...)

public class ClassName <T, K> {}
public interface InterfaceName<T, K> {}
// HashMap
public class HashMap<K, V> {}

 

생성시 구체적인 타입을 명시

public class ClassName <T, K> {}
public static void main(String[] args) {
    
    ClassName<String, Integer> a = new ClassName<String, Integer>();
}

TString이 되고, KInteger가 된다.

이때, 타입 파라미터로 명시할 수 있는 것은 참조 타입(Reference Type)밖에 올 수 없다. 즉, int, double, char 같은 primitive type은 올 수 없다. 그래서 int형 double형 등 primitive Type의 경우 Integer Double 같은 Wrapper Type으로 쓰는 이유가 바로 이런 이유이다.


제네릭 메소드

제네릭 타입을 메소드 리턴 타입 앞에 선언한 메소드를 제네릭 메소드라 한다.

public class Car<T> {
    
    private final T name;
    public Car(T name) {
        this.name = name;
    }
    
    public static <M> Car<M> from(M name) {
        return new Car<>(name);
    }
    
    public <M> void printEqualsCar(Car<M> car) {
        System.out.println(this.equals(car));
    }
}

Car 클래스에서 from메소드, printEqualsCar 메소드가 제네릭 메소드이다. 제네릭 타입은 구분을 위해 컨벤션 타입에 맞지 않는 M으로 지정함.

 

  • 제네릭 메소드 타입 <M>를 리턴 타입 앞에 붙여서 표시.  메소드 리턴 타입과는 다른 영역임.
  • 클래스에 선언한 <T>와 ㅈ네릭 메소드에 붙인 <M>과는 관련이 없다.
  • 제네릭 메소드에 붙인 <M>과 같은 타입은 해당 메소드에서만 적용되는 제네릭 타입이다.

 

public static Car<M> from(M name) {    // 컴파일 에러
    return new Car<>(name);
}

다음과 같이 선언시 컴파일 에러 발생

 

*제네릭 클래스가 아닌 일반 클래스에서도 제네릭 메소드를 사용할 수 있다.


제네릭 타입 제한

제네릭은 원하는 타입이 있을 때도 모든 타입이 들어올 수 있다.

ublic class Car<T> {
    private final T name;

    public Car(T name) {
        this.name = name;
    }
    ...
}
public static void main(String[] args) {
    Car<Integer> car = new Car<>(1);
}

Car 클래스 인스턴스 변수 name은 문자 관련 타입 지정되기를 원해도 다음과 같이 아무 타입이나 들어올 수 있다.

 

public class Car<T extends CharSequence> {
    private final T name;

    public Car(T name) {
        this.name = name;
    }
    ...
}

 

extends 키워드를 사용하여 타입을 제한시킬 수 있다.

CharSequence 인터페이스 하위 객체들만 데이터 타입으로 지정할 수 있게 함으로써 인스턴스 변수 name에 문자열 관련 타입만 지정할 수 있다.

 

public static void main(String[] args) {
    Car<String> car = new Car<>("sports");
}

제네릭 제거 시기

코드에 선언되고 사용된 제네릭 타입은 컴파일 시 컴파일러에 의해 자동으로 검사되어 타입 변환된다.

그리고 코드 내의 모든 제네릭 타입은 제거되어, 컴파일 된 class 파일에는 어떠한 제네릭 타입도 포함되지 않게 된다.

 

참조 :

'JAVA' 카테고리의 다른 글

[JAVA] aws s3 image upload  (0) 2023.08.14
[JAVA] List 마지막 값으로 덮어 씌워지는 문제  (0) 2023.07.31
[JAVA] GC : Garbage Collection  (0) 2023.04.16
[JAVA] 메모리 관리  (0) 2023.04.14
[Design Pattern] Facade Pattern  (0) 2023.04.06

관련글 더보기