본문 바로가기

Language/Java

제네릭(Generic)

반응형

자바나 코틀린으로 개발하다보면 내부함수들을 확인할 때가 있는데 상당부분 제네릭 타입으로 만들어져 있다.

다른 개발자들은 많이 사용하는 것 같은데 나는 코드 리딩만 해보았지 제네릭을 염두에 두고 코드를 설계한 적이 없었다.

그래서 이 참에 제네릭 개념도 다시 정리하고 테스트 코드도 작성하여 개발 파이를 넓혀보려고 한다.

 

Generic이란?

제네릭이란 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에서 컴파일 할 때 타입 체크를 해주는 기능이다.

객체의 타입을 컴파일 할 때 체크하기 때문에 의도하지 않은 타입의 객체가 저장되거나 호출되는 것을 막을 수 있고 형변환의 번거로움을 줄일 수 있다.

코드로 보충 설명하자면

package test;

public class Person 
{
	Object m_name;
	
	void setName(Object name) 
	{
		this.m_name = name;
	}
	
	Object getName(Object name) 
	{
		return m_name;
	}
}
public class Test 
{
	public static void main(String [] args)
	{
		Person p = new Person();
		p.setName(12345);
		if(p.getName() instanceof String)
		{
			String strName = String.valueOf(p.getName());
			System.out.println(strName.trim());
		} else {
			System.out.println("원하는 타입이 아닙니다.");
		}
	}
}

위 Person Class를 사용하려면 의도하지 않는 타입이 들어갈 수 있기 때문에 사용할 때 마다 타입체크를 해야 한다는 번거로움이 생길 수 있다.

 

Generic과 타입변수

 

Object로 만든 Person 클래스를 제네릭 클래스로 바꾸어 보겠다.

public class Person<T>
{
	T m_name;
	
	void setName(T name) 
	{
		this.m_name = name;
	}
	
	T getName(T name) 
	{
		return m_name;
	}
}

 

클래스 앞에 <T>를 추가해주고 Object로 되어 있는 부분을 T로 바꾸어 주면 된다.

 

여기서 T는 타입 변수의 한 종류이며 Type의 첫 글자에서 따온 것이다.

타입 변수의 종류는 다음과 같다.

T : Type(유형)

E : Element(요소)    ex) ArrayList<E>

K : Key(키)            ex) Map<K, V>

V : Value(값)         ex) Map<K, V>

 

코드를 Object에서 제네릭 타입으로 변경하게 되면 상황에 맞게 타입을 지정하여 사용할 수 있다.

public class Test 
{
	public static void main(String [] args)
	{
		Person<String> p = new Person<String>();
		p.setName("Hinos");
		System.out.println(p.getName());
		
		Person<Integer> p2 = new Person<Integer>();
		p2.setName(119);
		System.out.println(p2.getName());
	}
}

 

만약 지정한 타입과 일치하지 않다면

public class Test 
{
	public static void main(String [] args)
	{
		Person<String> p = new Person<String>();
		p.setName(119);
		System.out.println(p.getName());
	}
}

타입이 다르다는 에러가 발생하게 된다.

 

Generic에서 많이 실수하는 유형

- 타입 변수는 인스턴스 변수이기 때문에 Static에서 사용할 수 없다.

인스턴스 변수는 클래스의 인스턴스가 생성될 때 만들어지므로 Static 관점으로는 아직 메모리에 올라 오지 않는 인스턴스를 참조하려는 것이기 때문에 에러가 발생한다.

public class Test 
{
	static T Person; //Error, T cannot be resolved to a type
	
	public static void main(String [] args)
	{
		Person<String> p = new Person<String>();
		p.setName(119);
		System.out.println(p.getName());
	}
}

 

- 제네릭 타입의 배열을 생성할 수 없다. 

public class Person<T>
{
	private T m_name;
	private T[] m_arr = new T[5]; // Error, Unresolved compilation problem: Cannot create a generic array of T
	
	void setName(T name) 
	{
		this.m_name = name;
	}
	
	T getName() 
	{
		return m_name;
	}
	

	int arrSize()
	{
		return m_arr.length;
	}
}

언뜻보면 당연히 될 것 같은데 아쉽게도 이 코드는 에러가 발생한다.

왜냐하면 new 연산자는 컴파일 시점에 타입 T가 정확히 무엇인지 알아야 한다.

그런데 위의 코드에 정의된 new T<5>는 컴파일하는 시점에서는 T가 어떤 타입이 될지 전혀 알 수 없기 때문이다.

제네릭 배열을 생성해야될 경우 new 연산자 대신 Reflection API의 newInstance()와 같이 동적으로 객체를 생성하는 메서드로 배열을 생성하거나 Object 배열을 생성해서 복사한 다음에 T[]로 형변환하는 방법을 사용한다고 한다.

 

 

 

Generic 제한

- 제한된 제네릭 클래스

말 그대로 제네릭 타입의 범위를 정하고 범위에 벗어나는 타입들을 제한하는 것이다.

public class Person<T extends PersonalInfo>
{
	private T m_info;
	
	public Person(T info) {
		m_info = info;
	}
	
	void setInfo(T info) {
		m_info = info;
	}
	
	T getInfo() {
		return m_info;
	}
}

위 코드와 같이 타입 변수 뒤에 extends와 PersonalInfo 타입을 설정하면 PersonInfo 클래스 자신과 자신을 상속 받은 클래스들만 타입을 설정할 수 있다.

public class PersonalInfo 
{
	String m_name;
	int m_age;
	
	public PersonalInfo(String name, int age)
	{
		m_name = name;
		m_age = age;
	}
	
	public String getName() 
	{
		return m_name;
	}
	public void setName(String name) 
	{
		this.m_name = name;
	}
	
	public int getAge() 
	{
		return m_age;
	}
	public void setAge(int age) 
	{
		this.m_age = age;
	}
}

 

public class Test 
{
	public static void main(String [] args)
	{
		Person<PersonalInfo> p = new Person(new PersonalInfo("Hinos", 100));
		System.out.println(p.getInfo().m_name);
		System.out.println(p.getInfo().m_age);
	}
}
반응형

'Language > Java' 카테고리의 다른 글

Java로 자료구조(LinkedList, Stack, Queue) 구현해보기  (0) 2021.04.26
자바 == 연산자  (0) 2021.04.12
Interface Comparable  (0) 2020.11.29
Java Cipher  (0) 2019.10.24
자바 synchronized  (0) 2019.08.20