본문 바로가기
Programming/Java

[JAVA] 제네릭(Generic)

by prinha 2020. 8. 2.
반응형

 

제네릭(generic) 이란?

자바에서의 제네릭이란 데이터 타입(data type)을 일반화한다(generalize)는 것을 의미한다. (JDK 1.5부터)

클래스나 메소드에서 사용할 내부 데이터 타입을 컴파일 시에 미리 지정하는 방법이다.

 

제네릭의 장점
1. 객체의 타입 안정성을 높일 수 있다.
2. 타입 체크와 형변환을 생략할 수 있어 코드가 간결해진다.

1) 제네릭의 용어

 

2) 제네릭의 선언 및 생성

 - 클래스를 작성할 때, Object타입 대신 T와 같은 타입변수를 사용한다.

// Object 타입
class Box{
	Object item;
    
	void setItem(Object item){this.item=item;}
	Object getItem(){return item;}
}


// Generic 타입
class Box<T>{
	T item;
    
	void setItem(T item){this.item=item;}
	T getItem(){return item;}
}

 

- 참조 변수, 생성자에 T대신 실제 타입을 지정하면 형변환 생략이 가능하다.

Box<String> b = new Box<String>(); //타입 T대신에 실제 타입 지정(래퍼클래스)
b.setItem(new Object()); // !!에러, String이외의 타입은 지정불가
b.setItem("ABC"); // String 타입이므로 가능
String item=(String) b.getItem(); // 형변환이 필요없음

  

 

package com.generic.java;
import java.util.ArrayList;

class LandAnimal{
	public void crying() {
		System.out.println("육지동물");
	}
}
class Cat extends LandAnimal{
	@Override
	public void crying() {
		System.out.println("냐옹냐옹");
	}
}
class Dog extends LandAnimal{
	@Override
	public void crying() {
		System.out.println("멍멍");
	}
}
class Sparrow{

	public void crying() {
		System.out.println("짹짹");
	}
}

class AnimalList<T>{
	ArrayList<T> al = new ArrayList<T>();
	
	void add(T animal) {
		al.add(animal);
	}
	T get(int index) {
		return al.get(index);
	}
	boolean remove(T animal) {
		return al.remove(animal);
	}
	int size() {
		return al.size();
	}
}

public class Generic01 {
	public static void main(String[] args) {
		// java se 7부터 타입 생략가능
		AnimalList<LandAnimal> la = new AnimalList<>(); 

		la.add(new LandAnimal());
		la.add(new Cat());
		la.add(new Dog());
		
		//Sparrow는 클래스 타입이므로 에러
		//la.add(new Sparrow());
		
		for(int i=0; i<la.size(); i++) {
			la.get(i).crying();
		}
	}
}
/* 출력결과
육지동물
냐옹냐옹
멍멍
*/

 

 

3) 제네릭의 제한

 - static멤버에는 타입 변수 T를 사용할 수 없다.

class Box<T>{
	static T item; //에러
    static int compare(T t1, T t2){...} //에러
}

 

- 제네릭 타입의 배열 T[]를 생성하는 것은 허용되지 않는다.

class Box<T>{
	T[] itemArr; // T타입의 배열을 위한 참조 변수는 가능
    
    T[] toArray(){
    	T[] tmpArr = new T[itemArr.length]; // !!에러, 제네릭 배열 생성불가
        return tmpArr;
    }
}

 

- 참조 변수와 생성자에 대입된 타입이 일치해야한다. ( java se7부터 대입하는 생성자 생략 가능)

Box<Apple> appleBox = new Box<Apple>(); // 가능
Box<Melon> melonBox = new Box<>(); // 가능
Box<Apple> appleBox = new FruitBox<Apple>(); // 가능 -> 상속/다향성
Box<Apple> appleBox = new Box<Grape>(); // !!에러, 대입된 타입이 다름

 

 

4) 와일드카드

 - wild card란 이름에 제한을 두지 않음을 표현하는 데 사용되는 기호이다.

 - 제네릭 타입에 와일드 카드를 쓰면 여러 타입의 대입이 가능하다.

 - 자바의 제네릭에서는 물음표 기호를 사용하여 와일드 카드를 사용할 수 있다.

 

package com.generic.java;

import java.util.ArrayList;

class LandAnimal{
	public void crying() {
		System.out.println("육지 동물");
	}
}

class Cat extends LandAnimal{
	public void crying() {
		System.out.println("냐옹 냐옹");
	}
}

class Dog extends LandAnimal{
	public void crying() {
		System.out.println("멍멍");
	}
}

class Sparrow{
	public void crying() {
		System.out.println("짹짹");
	}
}

class AnimalList<T>{
	ArrayList<T> al = new ArrayList<T>();
	
	public static void cryingAnimalList(AnimalList<? extends LandAnimal> al) {
		LandAnimal la = al.get(0);
		la.crying();
	}
	
	void add(T animal) { al.add(animal); }
	T get(int index) { return al.get(index); }
	boolean remove(T animal) { return al.remove(animal); }
	int size() { return al.size(); }
}


public class Generic02 {
	public static void main(String[] args) {
		AnimalList<Cat> catList = new AnimalList<Cat>();
		catList.add(new Cat());
		AnimalList<Dog> dogList = new AnimalList<Dog>();
		dogList.add(new Dog());
		
		AnimalList.cryingAnimalList(catList);
		AnimalList.cryingAnimalList(dogList);
	}
}

 

 

5) 제네릭 메소드

 - 메소드의 선언부에 타입 변수를 사용한 메소드를 의미한다.

 - 타입 변수의 선언은 메소드 선언부에서 반환 타입 바로 앞에 위치해야한다.

public static <T> void sort(..) {..}

 - 클래스의 타입 매개변수<T>와 메소드 타입 매개변수<T>는 별개임을 주의해야한다.

class AnimalList<T> {
    ...
    public static <T> void sort(List<T> list, Comparator<? super T> comp) {
        ...
    }
    ...
}

 

 

6) 제네릭의 제거 시기 및 제거 방법

 자바 코드에서 선언되고 사용된 제네릭 타입은 컴파일 시 컴파일러에 의해 자동으로 검사되어 타입 변환된다.

 그리고서 코드 내의 모든 제네릭 타입은 제거되어, 컴파일된 class파일에는 어떠한 제네릭 타입도 포함되지 않게 된다.

 이런 식으로 동작하는 이유는 제네릭을 사용하지않는 코드와의 호환성을 유지하기 위해서이다.


출처 및 참고

자바의 정석

http://tcpschool.com/java/java_generic_concept

 

 

 

반응형