Programming/Java

[Java] 제네릭 클래스 (Generic Class)

Isaac S. Lee 2023. 8. 3. 15:24

제네릭 클래스는 자료형을 <T> 타입 변수로 대체한다. 때문에 클래스가 선언될 때 까지는 자료형 결정이 안 되었다가 객체가 생성될 때 결정이 된다.

제네릭 클래스를 작성하려면 타입 변수 인자 리스트가 필요하다.

 

  • T: 타입 변수 (메소드의 '매개 변수' 역할)
  • <>: 인자 리스트 (메소드의 '()' 역할)

제네릭 클래스의 T는 타입 변수로, 이는 메소드의 '매개 변수' 역할을 하며, 인자 리스트는 <>로, 메소드의 '()' 역할을 한다.

타입 변수는 자료형을 전달하는 변수로서 자료형을 마치 매개 변수처럼 취급하고 반드시 참조형만 가능하다는 특징이 있다. 또 타입 변수는 대소문자를 가리지 않으며, aaa, bbb로 작성할 수도 있지만, 보통 1글자 대문자로 적는다.

 

💡제네릭 클래스의 특징


public class Ex_Generic {
	public static void main(String[] args) {
		Item<String> item1 = new Item<String>(); // String
		item1.c = "문자열";
		
		Item<Integer> item2 = new Item<Integer>(); // Integer
		item2.c = 100;
		
		Pen<Boolean> pen = new Pen<Boolean>();
		pen.a = true;
		pen.b = false;
		pen.c = true;
		
		Note<String,Integer> note = new Note<String,Integer>("Isaac", 20);
		System.out.println(note.getA()); // String
		System.out.println(note.getB()); // Integer
	}
}

Item, Pen, Book, Note 제네릭 클래스를 생성하였다.

각각의 제네릭 클래스를 살펴보며 어떤 특징을 가지고 있는지 알아보도록 하자!

 

Item, Pen 제네릭 클래스

class Item<T> {
	public int a;
	public int b;
	public T c;
}

class Pen<T> {
	public T a;
	public T b;
	public T c;
}

제네릭 클래스는 자료형이 들어갈 곳에 타입변수를 적었다. 그래서 호출하는 자료형에 따라서 클래스 내의 T자료형은 String이 되기도 하고, Integer가 되기도 한다.
제네릭 클래스는 어떤 것이든 다 넣을 수 있다는 점에서 Object 변수와 비슷하지만 Object와의 차이점은 제네릭 클래스는 한 번 정하면 죽을 때까지 자료형이 고정된다는 점이다.

즉, 제네릭 클래스는 런타임 시에 한 번 자료를 픽스하는 순간 지속된다는 특징이 있다.

 

Book 제네릭 클래스

class Book<T> {
	public T a; // 멤버 변수의 자료형
	
	public void set(T a) { // 메소드 매개변수의 자료형
		this.a = a;
		T b; // 지역변수의 자료형
	}
	
	public T get() {
		return this.a; // 메소드 반환타입
	}
}

그렇다면 제네릭 클래스의 타입 변수는 어느 위치에 사용할 수 있을까?

멤버 변수의 자료형, 메소드 매개변수의 자료형, 지역변수의 자료형, 메소드 반환타입 등 자료형이 들어가는 부분은 모두 타입 변수를 쓸 수 있다.

그리고 지역변수의 자료형으로 사용할 수는 있으나 권장되지 않는다는 점에 주의하자.

 

Note 제네릭 클래스

class Note<T, U> {
	public T a;
	public U b;
	
	public Note(T a, U b) {
		this.a = a;
		this.b = b;
	}
	
	public T getA() {
		return this.a;
	}
	
	public U getB() {
		return this.b;
	}
}

타입 변수는 한 클래스 내에 여러 개 쓸 수 있다. 객체를 만들 때에도 타입 변수의 개수에 맞게 자료형을 써주면 된다. 그러나 타입변수는 많아야 2개, 보통은 1개를 쓰는 게 일반적이다.

 

💡제네릭 클래스의 활용


public class Ex_Generic {
	public static void main(String[] args) {
		// 기본 클래스
		WrapperInt n1 = new WrapperInt(100);
		System.out.println(n1.toString()); // [data=100]
		System.out.println(n1.getData() * 2); // 200
		System.out.println();
		
		// Object 클래스
		WrapperObject n2 = new WrapperObject(200);
		System.out.println(n2.toString()); // WrapperObject [data=200]
		System.out.println((int)n2.getData() * 2); // 400
		System.out.println();

		// Generic 클래스
		Wrapper<Integer> n3 = new Wrapper<Integer>(300);
		System.out.println(n3.toString()); // Wrapper [data=300]
		System.out.println(n3.getData() * 2); // 600
		System.out.println();
        }
}

 

기본 클래스 사용

class WrapperInt {
	
	private int data;
	
	public WrapperInt(int data) {
		this.data = data;
	}

	public int getData() {
		return data;
	}

	public void setData(int data) {
		this.data = data;
	}

	@Override
	public String toString() {
		return "[data=" + data + "]";
	}
}

 

Object 클래스 사용

 

Java Object 클래스 밝혀지는 출생의 비밀😮

Object 클래스는 JDK에서 제공하는 클래스로, 사용 빈도가 높지는 않지만 자바를 통틀어 가장 중요한 클래스이다. 눈으로는 보이지 않는 경우가 많은데, 우리가 사용하는 것에 밀접하게 관계되어

isaac-christian.tistory.com

// Object
class WrapperString {
	
	private String data;
	
	public WrapperString(String data) {
		this.setData(data);
	}

	public String getData() {
		return data;
	}

	public void setData(String data) {
		this.data = data;
	}

	@Override
	public String toString() {
		return "[data=" + data + "]";
	}
}

 

제네릭 클래스 사용

// Generic
class Wrapper<T> {

	private T data;
	
	public Wrapper(T data) {
		// 생성자는 꺽새 타입 변수 <T>를 붙이지 않는다.
		this.setData(data);
	}
	
	public T getData() {
		return data;
	}
	
	public void setData(T data) {
		this.data = data;
	}
	
	@Override
	public String toString() {
		return "Wrapper [data=" + data + "]";
	}
	
}

Object 클래스는 루트 클래스이므로 지구상의 모든 데이터를 넣을 수 있다는 장점이 있지만, 제네릭 클래스와 비교하면 상대적으로 데이터를 꺼내 쓸 때 여기에 뭐가 들어있는지 확인이 어렵다는 단점이 있다.

그런 Object 클래스의 불편함을 어느정도 해결할 수 있는 게 바로 Generic 클래스이다!

제네릭 클래스를 사용하면 데이터를 꺼내 쓸 때 타입 형변환을 거치지 않아도 되므로 코드 재사용성이 훨씬 높아지게 된다. 그 결과로 코드의 가독성을 높이고 런타임 오류를 최소화할 수 있다.

 

제네릭 클래스의 자료형 지정

제네릭 클래스는 앞서 객체의 자료형이 Integer로 객체를 생성한 게 확인되었기 때문에 getData() 메소드에 마우스를 올려보면 Integer로 자료형이 설정되어 있는 것을 확인할 수 있다.