자바/지네릭스

지네릭스의 전반적 개념(1)

백_곰 2022. 5. 11. 10:57

1. 지네릭스란?

- 지네릭스는 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 대해서 타입 체크를 해주는 기능을 말한다. 

 

- 그러므로, 객체의 타입 안정성을 높이고 형변환의 번거로움을 줄어든다.

 

- 타입 안정성을 높이는 것은 의도치 않은 타입의 객체가 저장되는 것을 막고, 저장된 객체를 꺼내올 때 원래의

타입과 다른 타입으로 잘못 형변환되어 발생할 수 있는 오류를 줄여준다는 뜻이다.

 

- 예를 들면, ArrayList 컬렉션 클래스에 다양한 객체를 담을 수 있지만 한 종류의 객체를 담는 경우가 많다. 그런데도

꺼낼 때 마다 타입 체킹을 하거나 사용자가 원하지 않는 종류의 객체를 저장하는 것을 막지 못하는 불편한 점이 있다.

그러한 것을 해결할 수 있는 것이 바로 '지네릭스'이다.

 

 

- 아래는 지네릭스를 적용 전과 후를 보여준다.

적용 전 적용 후
class Box{
     Object item;
     
     void setItem(Object item) { this. item = item; }
     Object getItem(){ return item; }
}
class Box<T>{
     T item;
     
     void setItem(T item) { this. item = item; }
     T getItem(){ return item; }
}

 

( 이때, Box<T>에서 T를 '타입 변수' 라고 한다. )

( T를 무조건 쓸 필요 없으며, K이나 V 또는 알파벳 어느 것이든 쓰면 된다. )

( 되도록이면 의미 있게 쓰이도록 하자. ex) Map<K,V> )

 

 

( 그래서 위 적용 후의 Box 클래스의 생성자는 아래와 같이 생성하면 된다. )

Box<String> b = new Box<String>();
// T 대신 String으로 타입 지정

// b.setItem(new Object());
// String 이외 다른 타입 불가능 에러

b.setItem("ABC");
// String이므로 정상

String item = b.getItem();
// 지네릭스 전에는
// String item = (String) b.getItem()
// 이였음.

 

 

( 지네릭스는 JDK 1.5에서 처음 도입된 것이기 때문에, 이전 코드와 호환을 위해 생성자를 생성할 때 타입 변수에

타입을 지정하지 않아도 컴파일이 가능하다. )

( 그러나, 아래와 같이 경고가 발생하게 된다. )

Box b = new Box();
// T는 Object로 간주. 정상

b.setItem("ABC")
// 경고발생

b.setItem(new Object());
// 경고발생

 

 

 

 

2. 지네릭스의 용어

class Box<T>{}

 

( 여기서 Box<T>지네릭스 클래스라고 부른다. T의 Box라고도 불린다. )

( T는 Box<T>의 타입변수 또는 타입 매개변수라 부른다. )

( Box원시타입이라고 부른다. )

 

 

Box<String> b = new Box<String>();

 

( <String>대입된 타입 즉, 매개변수화된 타입이라고 부른다. )

( Box<String>지네릭 타입 호출이라고 부른다. )

 

 

 

 

3. 지네릭스의 제한

 

- 지네릭스에서 불가능한 것이 아래와 같이 두 가지가 있다.

(1) 모든 객체에 대해 동일하게 동작해야하는 static 멤버에 타입 변수 T를 사용할 수 없다.

: 이유는 타입 문자 T는 인스턴스 변수로 간주하며, static 멤버는 항상 대입된 타입의 종류에 관계없이

동일해야 하기 때문이다.

 

 

(2) 지네릭 타입의 배열을 생성하는 것도 허용되지 않는다. 배열 타입의 선언은 가능하지만, 'new T[10];'

같은 것은 불가능하다.

: 이유는 new 연산자 때문인데, 이 연산자는 컴피일 시점에 타입 T가 뭔지 정확히 알아야 한다.

: 그러나, new T[10]을 클래스 로드하는 과정에서 선언했기 때문에 불가능하다.

 

( 꼭 지네릭 배열을 생성해야할 필요가 있을 때는, new 연산자 대신 'Reflection API'의 newInstance()와 같이

동적으로 객체를 생성하는 메서드로 배열을 생성하거나, 아래와 같이 생성한다. )

 

 

 

 

4. 지네릭 클래스의 객체 생성과 사용

- Box<T> 같은 지네릭 클래스가 있다고 가정해보자. 

 

( 그렇다면, 참조변수와 생성자에 대입된 타입이 일치해야 한다. 그렇지 않을 경우 에러가 발생한다. )

( 또한 상속 관계에서도 에러가 발생한다. 아래의 코드를 보고 이해하자. )

Box<Apple> appleBox = new Box<Apple>();
// 가능

Box<Apple> appleBox = new Box<Grape>();
// 에러

Box<Fruit> appleBox = new Box<Apple>();
// 에러

 

 

( 그러나, 아래와 같이 두 지네릭 클래스의 타입이 상속관계에 있고, 대입된 타입이 같은 것은 가능하다. )

( 다형성이 가능하다는 것이다. )

Box<Apple> appleBox = new FruitBox<Apple>();
// 가능. 다형성

 

 

( 또한 JDK 1.7부터 추정이 가능한 경우 타입을 생략할 수 있게 되었다. )

( 아래와 같이 두 코드는 동일하게 인식한다. )

Box<Apple> appleBox = new Box<Apple>();
// 위 아래 동일한 코드
Box<Apple> appleBox = new Box<>();

 

 

( 조심해야 할 것은 아래와 같이 자신이 대입한 타입에만 객체를 생성할 수 있다. )

Box<Apple> appleBox = new Box<Apple>();
appleBox.add(new Apple());
// 정상

appleBox.add(new Grape());
// 에러 발생

 

 

( 그러나 타입 T가 자신의 조상인 Fruit인 경우, ArrayList의 void add(Fruit item) 메서드에서는 아래와 같이

정상적으로 컴파일 할 수 있다. )

Box<Fruit> fruitBox = new Box<Fruit>
fruitBox.add(new Fruit());
// 가능

fruitBox.add(new Apple());
// 가능

 

 

 

 

4-1. 지네릭 클래스의 객체 생성과 사용을 이해하기 위한 예제(1)

: 위에서 봤던 개념을 적용한 예제이다.

 

import java.util.ArrayList;

public class Exercise001 {	
	public static void main(String[] args){
		Box<Fruit> fruitBox = new Box<Fruit>();
		Box<Apple> appleBox = new Box<Apple>();
		Box<Toy>   toyBox   = new Box<Toy>();
//		Box<Grape> grapeBox = new Box<Apple>(); // 에러. 타입 불일치

		fruitBox.add(new Fruit());
		fruitBox.add(new Apple()); // OK. void add(Fruit item)

		appleBox.add(new Apple());
		appleBox.add(new Apple());
//		appleBox.add(new Toy()); // 에러. Box<Apple>에는 Apple만 담을 수 있음

		toyBox.add(new Toy());
//		toyBox.add(new Apple()); // 에러. Box<Toy>에는 Apple을 담을 수 없음

		System.out.println(fruitBox);
		System.out.println(appleBox);
		System.out.println(toyBox);
	}
}

class Box<T> {
	ArrayList<T> list = new ArrayList<T>();
	void add(T item)  { list.add(item); }
	T get(int i)      { return list.get(i); }
	int size() { return list.size(); }
	public String toString() { return list.toString();}
}

class Fruit				  { public String toString() { return "Fruit";}}
class Apple extends Fruit { public String toString() { return "Apple";}}
class Grape extends Fruit { public String toString() { return "Grape";}}
class Toy		          { public String toString() { return "Toy"  ;}}

 

 

 

 

다음장

지네릭스의 전반적 개념(2) (tistory.com)

 

지네릭스의 전반적 개념(2)

 

kind-coding.tistory.com