제네릭(Generic)

최원준·2021년 9월 28일
0

1.왜 제네릭을 사용해야 하는가?


Java 5 부터 제네릭(Generic) 타입이 새로 추가되었는데, 제네릭 타입을 이용함으로써 잘못된 타입이 사용될 수 있는 문제를 컴파일 과정에서 제거할 수 있게 되었다. 제네릭은 컬렉션, 람다식, 스트림, NIO에서 널리 사용되므로 확실히 이해해 두어야 한다.

제네릭은 클래스와 인터페이스, 그리고 메소드를 정의할 때 타입(type)을 파라미터(parameter)로 사용할 수 있도록 한다.

제네릭 클래스 : parameterized Class 된 클래스.

제네릭한 알고리즘은 문법적으로 다형성(polymorphism)을 이용한 것이다. 상속, 다형성, 추상 클래스, 인터페이스 등을 이용해서 제네릭한 클래스를 구현하기 위한 것이 제네릭이다.


컴파일시 강한 타입 체크를 할 수 있다.

자바 컴파일러는 코드에서 잘못 사용된 타입 때문에 발생하는 문제점을 제거하기 위해 제네릭 코드에 대해 강한 타입 체크를 한다. 실행시 타입 에러가 나는 것보다는 컴파일 시에 미리 타입을 강하게 체크해서 에러를 사전에 방지하는것이 좋다.

타입 변환(casting)을 제거한다.

비제네릭 코드는 불필요한 타입 변환을 하기 때문에 프로그램 성능에 악영향을 미친다.

	List list = new ArrayList();
   	list.add("hello");
	String str = (String) list.get(0)	// casting을 해야한다.

다음과 같이 제네릭 코드로 수정하면 List에 저장되는 요소를 String 타입으로 국한하기 때문에 요소를 찾아올 때 타입변환을 할 필요가 없어 프로그램 성능이 향상된다.

	List<String> list = new ArrayList<String>();
    	list.add("hello");
    	String str = list.get(0);		//타입 변환을 하지 않는다.

2. 제네릭 타입(Class<T>, interface<T>)


제네릭 타입은 타입을 파라미터로 가지는 클래스와 인터페이스를 말한다. 아래의 코드에서 타입 파라미터의 이름은 T 이다.

public class 클래스명<T> {...} 
pulic interface 인터페이스명<T> {...}

제네릭 타입을 실제 코드에서 사용하려면 타입 파라미터에 구체적인 타입을 지정해야 한다.
왜 이런 타입 파라미터를 사용해야 할까?

Java에서 제네릭의 개념이 생기기 이전에는 Object type으로 작성하면 비슷하게 사용할 수 있다. 그런데 이렇게 Object Class를 사용하면 데이터를 get 할 때 반드시 type casting이 필요하다. 이 경우는 type safe한 방식이 아니다. 그래서 지금은 Generic을 사용하는 방식을 선호한다.


3. 멀티 타입 파라미터(Class<K,V,...>, interface<K,V,...>)


제네릭 타입은 두 개 이상의 타입 파라미터를 사용할 수 있다. 이렇게 멀티로 작성하다보면 소스가 지저분해질 수 있는데 자바 7 부터는 다이아몬드 연산자를 사용해서 간단히 작성할 수 있다.

	Product<Tv, String> product = new Product<TV, String>();	//Java 6 이전
	Product<Tv, String> product = new Product<>();			//Java 7 이후

4. 제네릭 메소드(<T,R> R method(T t))


제네릭 메소드는 매개 타입과 리턴 타입으로 타입 파라미터를 갖는 메소드를 말한다. 제네릭 메소드를 선언하는 방법은 리턴타입 앞에 <> 기호를 추가하고 타입 파라미터를 기술한 다음, 리턴 타입과 매개 타입으로 타입 파라미터를 사용하면 된다.
plublic <T> Box<T> boxing(T t){...}

제네릭 메소드는 두가지 방식으로 호출할 수 있다.

Box<Integer> box = <Integer>boxing(100);	//타입 파라미터를 Integer로 명시적으로 지정
Box<Integer> box = boxing(100);			//타입 파라미터를 Integer로 추정

5. 제한된 타입 파라미터(<T extentds 최상위타입>)

public <T extneds 상위타입> 리턴타입 메소드(매개변수, ...) {...}

타입 파라미터에는 지정되는 구체적인 타입을 제한할 필요가 종종 있다. 예를 들면 숫자를 연산하는 제네릭 메소드는 매개값으로 Number 타입 또는 하위 클래스 타입(Byte, Short, Integer, Long, Double)의 인스턴스를 가져야만 한다. 이것이 제한된 타입 파라미터가 필요한 이유이다.

제한된 타입 파라미터를 선언하려면 타입 파라미터 뒤에 extends 키워드를 붙이고 상위 타입을 명시하면 된다. 상위 타입은 클래스 뿐만 아니라 인터페이스도 가능하다. (단, 인터페이스라고 해서 implements를 사용하지는 않는다.)

public Class Util{
    public static <T extends Number> int compare(T t1, T t2){
        double v1 = t1.doubleValue();
        double v2 = t2.doubleValue();
        return Double.compare(v1, v2);
    }
}

pulic class Test{
    public static void main(String[] args){
        String str = Util.compare("a", "b"); 	//String은 Number 타입이 아니기 때문에 에러가 발생.
    
        int resutl1 = Util.compare(10,20);		//int를 Integer로 자동 Boxing
        int result2 = Util.compare(4.5, 3); 	//double을 Double로 자동 Boxing
    }
}

6. 와일드 카드 타입(<?>, <? extends ...>, <? super...>


코드에서 ?를 일반적으로 와일드카드(wildcard)라고 부른다. 제네릭 타입을 매개값이나 리턴 타입으로 사용할 때 구체적인 타입 대신 와일드카드를 다음과 같이 세 가지 형태로 사용할 수 있다.
  • 제네릭 타입<?>: Unbounded Wildcards(제한 없음)
    타입 파라미터를 대치하는 구체적인 타입으로 모든 클래스나 인터페이스 타입이 올 수 있다.

  • 제네릭타입<? extends 상위타입> : Upper Bounded Wildcards(상위 클래스 제한)
    타입 파라미터를 대치하는 구체적인 타입으로 상위 타입이나 하위 타입만 올 수 있다.

  • 제네릭타입<? super 하위타입> : Lower Bounded Wildcards(하위 클래스 제한)
    타입 파라미터를 대치하는 구체적인 타입으로 하위 타입이나 상위 타입이 올 수있다.


7. 제네릭 타입의 상속과 구현


제네릭 타입도 다른 타입과 마찬가지로 부모 클래스가 될 수 있다.

public class ChildProduct<T,M> extends Product<T,M> {...}

자식 제네릭 타입은 추가적으로 타입 파라미터를 가질 수 있다. 다음은 세 가지 타입 파라미터를 가진 제네릭 타입을 선언한 것이다.

public class CholdProduct<T,M,C> extends Product<T,M> {...}

그리고 제네릭 인터페이스를 구현한 클래스도 제네릭 타입이 된다.

profile
Lv.01 개발자

0개의 댓글