JAVA - 지네릭스(1)

DevSeoRex·2022년 11월 29일
0
post-thumbnail

지네릭스(Generics)

  • JDK 1.5에서 처음 도입되었다.
  • 다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 컴파일 시의 타입 체크를 해주는 기능이다.
  • 컴파일 시에 객체의 타입을 체크하여, 객체의 타입 안정성을 높이고 형변환의 번거로움을 줄여준다.

지네릭 클래스 선언

  • 지네릭 타입은 클래스와 메서드에 선언할 수 있다.
// 지네릭 적용 전의 코드
class Box {
	Object item;
    
    void setItem(Object item) { this.item = item; }
    Object getItem()		  { return item;	  }
}

위의 예제를 지네릭 클래스로 변경하려면, 클래스 옆에 <T>를 붙이고, 'Object'를 모두 <T>로 바꾸면 된다.

// 지네릭 적용 후의 코드
class Box<T> {   				// 지네릭 타입 T를 선언
	T item;
    
    void setItem(T item)	{ this.item = item; }
    T getItem()				{ return item;		}
}		
  • Box<T>에서 T를 타입 변수라고 한다.

지네릭스의 도입 이전에는 다양한 종류의 타입을 다루는 메서드의 매개변수나 리턴타입으로 Object 타입의 참조변수를 사용함으로 인해, 형변환이 불가피했다.
지네릭스가 도입 됨으로서, 타입을 지정하여 주기만 하면 형변환을 하는 번거로움을 덜 수 있다.

// 지네릭 클래스인 Box 클래스의 객체 생성 예제
Box<String> b = new Box<String>();		// 타입 T 대신, 실제 타입 지정
b.setItem(new Object());				// 에러. String 이외의 타입은 지정 불가
b.setItem("ABC");						// OK. String 타입이므로 가능
String item = b.getItem();				// 형변환이 필요없다.

💡 지네릭을 사용하지 않고 지네릭 클래스의 객체 생성은 가능하지만, 권장하지      않는다.

지네릭스의 용어

class Box<T> {}

위의 예시처럼 지네릭 클래스 Box가 있다.
지네릭 클래스는 크게 보면 지네릭 클래스, 타입 변수, 원시 타입 으로 이루어져 있다.
위의 예시에서 지네릭 클래스, 타입 변수, 원시 타입은 아래와 같다.

  • 지네릭 클래스 : Box<T>
  • 타입 변수(타입 매개변수) : T
  • 원시 타입 : Box

💡 지네릭 타입의 배열을 생성하는 것은 허용되지 않는다.

지네릭 클래스의 객체 생성 & 사용

// 지네릭 클래스의 객체 생성 예제
Box<Apple> appleBox = new Box<Apple>();		// OK
Box<Apple> appleBox = new Box<Grape>();		// 에러 -> 타입 불일치

// Fruit과 Apple이 상속관계일 경우의 예제
Box<Fruit> appleBox = new Box<Apple>();		// 에러. 대입된 타입이 다르다.

// 지네릭 클래스의 타입이 상속 관계에 있을때의 예제
Box<Apple> appleBox = new FruitBox<Apple>();	// OK. 다형성

💡 JDK 1.7부터는 추정이 가능한 경우 타입을 생략할 수 있다.

// 지네릭 클래스의 타입 생략 예제

// 타입 생략 전
Box<Apple> appleBox = new Box<Apple>();

// 타입 생략 후
Box<Apple> appleBox = new Box<>();

Box<T>클래스에 void add(T item)이라는 메서드가 있다고 할때, 대입된 타입과 다른 타입의 객체는 추가가 불가능하다.
그러나 예외도 있는데, 대입된 타입의 자손들을 추가가 가능하다.

// 타입 불일치 예제 
Box<Apple> appleBox = new Box<Apple>();
applebox.add(new Apple());		// OK
applebox.add(new Grape());		// 에러. Box<Apple>에는 Apple 객체만 추가 가능하다.

// 자손 추가 예제 (Fruit는 Apple의 조상이다)
Box<Fruit> fruitBox = new Box<Fruit>();
fruitBox.add(new Fruit());		// OK.
fruitBox.add(new Apple());		// OK. 	 void add(Fruit item)

제한된 지네릭 클래스

  • 타입 문자로 사용할 타입을 명시하면 한 종류의 타입만 저장할 수 있다.
  • 타입 문자로 사용할 타입을 명시해도 모든 종류의 타입을 지정할 수 있다는 것에는 변함이 없다.

지네릭 타입에 'extends' 키워드를 사용해서 특정 타입의 자손들만 대입할 수 있게 제한할 수 있다.

// 제한된 지네릭 클래스 예제
class FruitBox<T extends Fruit> {
	ArrayList<T> list = new ArrayList<T>();
    ....
}

// 모든 과일은 Fruit가 조상이다.
FruitBox<Apple> appleBox = new FruitBox<Apple>();	// OK.

// 에러. Toy는 Fruit의 자손이 아니다.
FruitBox<Toy>	toyBox 	 = new FruitBox<Toy>();		

💡 특정 인터페이스를 구현해야 한다는 제약도 사용할 수 있다.

interface Eatable {}

// Eatable 인터페이스를 구현한 클래스만 대입 가능하게 제한
class FruitBox<T extends Eatable>	{...}

// Eatable 인터페이스를 구현하고, Fruit의 자손인 클래스만 대입 가능하게 제한
class FruitBox<T extends Fruit & Eatable> {...}

💡 인터페이스를 구현해야 하는 제약을 줄때 implements가 아닌 extends
     키워드를 사용한다는 것에 주의하고, 조건이 두개 일경우 '&' 기호로
     연결해야한다.

출처 : 자바의 정석 3rd Edition(남궁성 저, 도우출판)

0개의 댓글