Java 제네릭(Generic) 이란 무엇인가?


자바8의 함수형 인터페이스를 보면 인자와 리턴 타입이 제네릭으로 지정되어 있는것을 볼 수 있습니다.

개인적으로 제네릭은 어렵다고 느끼고 있었는데, 그래서 더욱 개념을 명확히 잡는게 중요하다고 생각합니다.

그래서 이렇게 제네릭(Generic)이 무엇인지 알아보려고 합니다.


살펴볼 내용입니다.

  • 제네릭과제네릭을 사용해야 하는 이유

  • 제네릭 타입

  • 제네릭 메소드

  • 타입의 제한

  • 와일드 카드 타입


1. 제네릭이란 ? 

다양한 타입의 객체에 재사용을 높일 수 있는 기법으로 클래스에서 사용할 타입을 

외부에서 설정하는 것을 말합니다.

선언 시 클래스 또는 인터페이스 에 "<>" 를 붙이고 타입파라미터를 지정합니다.

제네릭을 사용하지 않을 경우 빈번한 타입 변환이 발생 할 수 있으며 이는 프로그램의 성능을 저하시킵니다.

아래의 예시를 살펴볼까요? 

Object 타입을 사용하여 클래스를 생성하였습니다. 


public class Car {
    private Object obj;
    public void set(Object obj) {this.obj = obj;}
    public Object get() {return obj;}
}


Car car = new Car();
car.set("Black");
String color = (String) car.get();		


하지만 Object 타입을 String 타입으로 강제 타입 변환해야 합니다.

또한 잘못된 타입을 사용될 수 있는 문제가 발생 할 수 있습니다.

따라서 제네릭을 사용하면 컴파일 시 타입 체크가 가능하고, 실행 시 타입 에러가 나는 것을 방지 할 수 있습니다.

이제 그럼 제네릭타입을 사용해서 적용해보겠습니다.


2. 제네릭타입

메소드 내부에서 사용할 타입을 외부에서 인자로 받을 건데요. 인스턴스를 생성하는 시점 또는 메소드를 호출하는 시점에

정할 수 있습니다. 이제 적용해 보겠습니다.


public class Car<T> {
    private T t;
    public void set(T t) {this.t = t;}
    public T get() {return t;}
}


먼저 Object 를 T로 바꿨고 해당 클래스에서 사용하는 T 라고 정의하고 인스턴스 생성시 받아서 정한다는 의미입니다.

그리고 이렇게 사용합니다.


Car<String> car1 = new Car<String>();
car1.set("Black");
String color = car1.get();		
		
Car<Integer> car2 = new Car<Integer>();
car2.set(3);
int colornum = car2.get();


클래스를 선언할 때 타입 파라미터를 사용하여 컴파일 시 타입 파라미터가 구체적인 클래스로 변경됩니다.

멀티 타입의 파라미터도 사용이 가능합니다.

제네릭 타입은 두개 이상의 타입 파라미터를 사용가능한데요

  • class<K, V, …> {  …  }
  • interface<K, V, …>  {  …  }
이러한 형식으로 사용이 가능합니다.

public class Car<T, M> {
    private T color;
    private M model;

    public void setcolor(T color) {this.color =color;}
    public void setmodel(M model) {this.model = model;}
    
    public T getcolor() {return color;}
    public M getmodel() {return model;}
}
Car<String, String> car = new Car<String, String>();
car.setcolor("Black");
car.setmodel("SUV");
String color = car.getcolor();

내부에서 사용할 타입을 외부에서 받아 올 때 들어올 수 있는 타입을 제한 할 수 있습니다.

class Car<T extends String, M extends Number> 

제네릭 클래스를 만들 때 T 를 사용했는데 T는 보통 Type 을 의미합니다.
원하는데로 만들어도 되지만 보통 자주 사용하는 타입 인자는 이러하니 참고하세요.


타입 인자 

의미

E

Element

K

Key

N

Number

T

Type

V

Value

R

Result


3. 제네릭 메소드

제네릭 메소드는 매개변수 타입과 리턴 타입으로 타입 파라미터를 갖는 메소르를 말합니다.

일반적으로 제네릭 클래스를 사용할 떄와 비슷합니다.

다만, 리턴 타입 앞에 타입 파라미터를 넣어야 하고 이 타입 파라미터는 리턴 타입과 매개변수에 사용됩니다.

먼저 제네릭 클래스를 만듭니다.


public class Car<T, M> {
    private T color;
    private M model;
	
    public Car(T color, M model) {
    	this.color = color;
	this.model = model;
    }
	
    public void setcolor(T color) {this.color =color;}
    public void setmodel(M model) {this.model = model;}
	
    public T getcolor() {return color;}
    public M getmodel() {return model;}
}


제네릭 메소드를 선언합니다.


public class genericSample {
	
    public static <T, V> boolean compare(Car<T, V> c1, Car<T, V> c2) {
        boolean colorCompare = c1.getcolor().equals(c2.getcolor()) ;
	boolean modelCompare = c1.getmodel().equals(c2.getmodel());
	return colorCompare && modelCompare;
    }
	
}


제네릭 메소드를 호출하는 방법 입니다.

명시적으로 지정할 수 도 있고 매개 값을 보고 타입을 추정 할 수 있습니다.


Car<String, String> c1 = new Car<String, String>("black", "SUV"); //타입 파라미터를 명시적으로 지정
Car<String, String> c2 = new Car("black", "SUV");  //타입 파라미터를 String 으로 추정
boolean result1 = genericSample.<String, String>compare(c1, c2);


4. 타입의 제한

타입 파라미터에 지정되는 타입이 제한할 필요가 있습니다.

제네릭 메소드에서도 타입을 제한 할 수 있습니다.

public static <T extends String, V extends String> boolean compare(Car<T, V> c1, Car<T, V> c2){...}

public static <T extends String, V extends Number> boolean compare(Car<T, V> c1, Car<T, V> c2){...}

상위 타입은 클래스와 인터페이스도 가능합니다.


5. 와일드 카드 타입

와일드카드 타입에는 세가지의 형태가 있습니다.

? 키워드로 표시됩니다.


먼저 제네릭타입<?>  입니다.

타입 파라미터를 대치 하는 것으로 모든 클래스나 인터페이스 타입이 올 수 있습니다.


public static boolean compare(Car<?> c1, Car<?> c2) {
    boolean colorCompare = c1.getcolor().equals(c2.getcolor()) ;
    return colorCompare;
}


제네릭타입<? extends 상위타입>

와일드카드의 범위를 특정 객체의 하위로 제한하는 것입니다.


public static boolean compare(Car<? extends String> c1, Car<? extends String> c2) {
    boolean colorCompare = c1.getcolor().equals(c2.getcolor()) ;
    return colorCompare;
}


제네릭타입<? super 하위타입>

위의 반대로 상위클래스만 올 수 있게 합니다.

public static boolean compare(Car<? super String> c1, Car<? super String> c2) {
    boolean colorCompare = c1.getcolor().equals(c2.getcolor()) ;
    return colorCompare;
}


이렇게 제한을 둘 경우 단지 클래스의 제한이 아닌 예상치 못한 에러를 컴파일 단계에서부터 찾아낼 수 있습니다.


그리고 인터페이스도 제네릭으로 구현 할 수 있습니다.

제네릭 인터페이스를 구현한 클래스 또한 제네릭 타입으로 구현해야 한다점을 기억해야 합니다.


이렇게 자바 제네릭의 개념에 대해 살펴보았습니다.

도움이 되셨다면 공감을 눌러주세요.


참고 : 이것이 자바다

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기