JAVA/JAVA Basic

[ JAVA ] 제네릭 (Generic)

IT록흐 2021. 6. 20. 09:30
반응형

 

 

제네릭(Generic)을 사용하는

이유는 무엇일까?

 

 

프로그래밍에서 자료형(Data Type)이란 굉장한 의미를 갖는다. 자료형이 '명시'되어 있으면 데이터를 저장하는데 필요한 메모리의 크기를 알 수 있다. 이처럼 자료형은 변수에 저장할 수 있는 데이터의 '범위'를 알려준다.

 

그러므로 코딩을 할 때, 데이터를 데이터에 알맞는 크기의 변수에 저장하면 모든 일이 쉽게 풀릴 것만 같다. 하지만 코딩은 그리 단순한 흐름으로 흘러가지 않는다.

 

double realNum = 1.5;

int intergers = (int) realNum;

 

 

double(8byte)형 변수에 저장한 데이터를 int(4byte)형 변수에 저장해야하는 상황이 발생한다. (강제형변환, casting)

 

강제형변환은 프로그램 성능에 악영향을 준다. 실수 1.5가 int형 변수로 Casting되면 값은 1이 된다. 데이터 손실이 발생하는 것이다. 그래서 Casting을 두고 Unsafe Conversion(안전하지 못한 변환)라고도 부른다. 그리고 단순한 대입이 아닌 (int)realNum과 같이 캐스팅 작업이 추가되니 성능에 좋은 영향을 주지 못한다.

 

원시타입(primitive type) int와 double를 예로 들었지만, 이는 참조 타입(reference type)에서도 적용되는 이야기다. 클래스는 부모 클래스와 자식클래스가 있다. 부모 클래스 참조변수에 자식 클래스 객체를 넣는 것을 자동형변환(Promotion), 자식 클래스 참조변수에 부모 클래스 객체를 넣는 것을 강제형변환(Casting)이라고 부른다.

 

이렇듯 Java에서는 성능에 좋지 못한 영향을 끼치는 강제형변환을 최소한으로 줄이는 방법을 만들었는데 그것이 바로 '제네릭(Generic)'이다.

 

제네릭(Generic)

 

JAVA에서는 Promotion(자동형변환)을 자주 사용한다. Promotion은 객체의 부품화를 통한 유지보수를 유용하게 만들기 때문이다. Promotion을 자주 사용하는 만큼 손실되는 데이터도 많다. 원시타입과 반대로 Promotion을 하면 데이터의 범위가 줄고 Casting을 해야 데이터의 범위가 늘어난다.

 

자식 클래스가 부모클래스의 메소드를 상속받기 때문이다. 그러므로 Promotion을 많이 하면 손실된 데이터를 복구해야하므로 그만큼 Casting의 빈도도 늘어난다. 그럼 Promotion과 Casting을 획기적으로 줄여줄 Generic을 사용해보자.

 

< Genenric 클래스 >

package genericstudy;

public class Box<T> { //< T >  타입 파라미터 이름을 T로 한다.

	private T t; // T를 자료형으로 갖는 변수
	
	public T get() { return t; } // T 를 반환타입으로 갖는 메소드
	public void set(T t) { this.t = t; } //T를 매개변수로 갖는 메소드

}

 

 

< Main 클래스 >

package genericstudy;

public class Main1 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Box<Integer> box1 = new Box<Integer>(); // T -> Integer
		Box<String> box2 = new Box<String>(); // T -> String
		
		box1.set(5); // 정수를 매개변수로 넣기
		Int a = box1.get(); // Casting 필요 없음
        System.out.println(a);
		
		box2.set("Hello"); // 문자열을 매개변수로 넣기
		String b = box2.get(); // Casting 필요 없음
        System.out.println(b);
	}
}

 

Generic을 사용하면 획기적인 코딩을 할 수 있다. 내가 만든 클래스는 1개이지만 타입이 전혀 다른 객체를 두 개를 생성했다. Generic은 보통 Stack, List, Queue 같은 자료구조 표준 API 클래스에 많이 사용된다. 자료구조 안에는 다양한 자료형의 데이터가 삽입될 수 있기 때문이다.

 

Generic을 사용하기 전까지는, Promotion을 자주 사용했다. 매개변수를 받을 때 Object 클래스로 받는 것이다. Object는 모든 클래스의 조상클래스이니 어떤 클래스도 실인수로 받을 수 있다.

 

package genericstudy;

public class Box {

	private Object t; 
	
	public void set(Object t) { this.t = t; } 
	public Object get() { return t; } 
}

 

이 구조의 문제점은 반환도 Object로 해야한다는 것이다. 그러므로 이를 사용하려면 다시 알맞는 자료형으로 Casting해주어야 한다.

 

String a = (String)(box1.get());

 

Promotion => Casting으로 가는 과정에서 데이터의 범위가 줄어 들었다가 늘어 났다가를 반복한다. 이런건 프로그램에 성능에 좋지 않은 영향을 준다. 그러므로 다양한 자료형의 데이터가 들어가 다양한 자료형의 객체를 생성하는 클래스는 Generic으로 만들어주는 것이 효율적이다.

 

class<T, M>, Interface<T,M>

 

Generic의 타입을 하나가 아닌 두 개까지 지정해줄 수 있다.

 

< Generic 클래스 >

package genericstudy;

public class Product<T, M> { // 타입 파라미터 T , M
	
	private T kind;
	private M model;
	
	public T getKind() {
		return kind;
	}
	public void setKind(T kind) {
		this.kind = kind;
	}
	
	public M getModel() {
		return model;
	}
	public void setModel(M model) {
		this.model = model;
	}
}

 

< Main 클래스 >

public class Main2 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Product<Integer, String> product = new Product();
		
		product.setKind(5);
		product.setModel("M31");
		
		System.out.println(product.getModel());
		System.out.println(product.getKind());
	}

}

 

 

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

 

클래스가 아닌 메소드에 제네릭 타입을 지정해줄 수 있다.

 

< T , R >(제네릭 메소드임을 알림) R (반환 타입) method(T t) (매개변수)

 

 

package genericstudy;

public class Util {
	public static <T> Box<T> boxing(T t){
		Box<T> box = new Box(); // Box 객체 생성
		box.set(t);
		return box; // Box 객체 반환
	}
}
package genericstudy;

public class Main3 {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		Box<Integer> box1 = Util.<Integer>boxing(100); // Integer 객체 생성 
		int intValue = box1.get(); 
		
		Box<String> box2 = Util.boxing("Hello"); // String 객체 생성
		String stringValue = box2.get();
		
        // 출력
		System.out.println(intValue);
		System.out.println(stringValue);
	}
}

 

정리

 

 

1. 반복되는 타입 변환을 막기 위해 Generic을 사용한다.

2. Generic의 타입은 2개 써줄 수 있다.

3. Generic 타입은 메소드에만 설정해줄 수 있다.

 

 

 


 

참고자료

 

 

 

 

반응형