위로 아래

제네릭

제네릭(Generics)

//기본형 선언
class 클래스이름<타입매개변수> {
	필드;
    메소드;
}


//제네릭 생성자 호출
제네릭클래스이름 <적용할타입(자료형)> 참조변수이름 = new 제네릭클래스이름 <>();


// 제네릭 클래스
public class 클래스이름 <T> { ... }

// 제네릭 인터페이스
public interface 인터페이스이름 <T> { ... }

// 제너릭 메소드
<타입매개변수> 반환타입 메소드이름 () { ... }
  1. 한 번의 정의로 여러 종류의 데이터 타입을 다룰 수 있도록 하는 방법 (하나의 코드를 다양한 타입의 객체에 재사용하는 객체 지향 기법)
  2. 자료형(타입)을 지정해주는 게 아니라, 변수로 사용한다. 
  3. 클래스, 인터페이스, 메소드를 정의할 때 쓸 수 있다.
  4. 자료형을 클래스 내부에서 지정하는 것이 아니라, 외부에서 사용자에 의해 지정되는 것.
  5. <T> 타입은 해당 블럭까지만 유효하다.
  6. 다이아몬드 연산자 <> : 생성자에서 괄호 안을 생략하면, 문맥으로 추론해서 앞의 타입을 넣는다.
  7. 잘못된 자료형(타입)이 지정되어 오류가 날 수 있는 것을 미리 컴파일 단계에서 방지할 수 있다.
  8. 예시 : ArrayList<E> 형태로 클래스가 정의되어 있으면, 사용자가 데이터로 String을 사용할 때엔 ArrayList<String>, Integer를 사용할 때엔 ArrayList<Integer>로 사용할 수 있다.

용어

  1. 타입 매개변수(type parameter) : Human <T>에서 T
  2. 타입 인수(type argument) : Human <man>에서 man
  3. 매개변수화 타입 (parameterized type) : Human <man>

 

제네릭 불가 사항

  1. 기초 타입을 제네릭 인수로 사용 불가
  2. 정적 제네릭 타입 금지
  3. 제네릭 타입의 배열 생성 금지

 

제네릭 타입 매개변수

  1. E : 원소 (Element)
  2. K : 키 (Key)
  3. N : 숫자 (Number)
  4. T : 타입 (Type)
  5. V : 값 (Value)

제네릭 클래스 예시

더보기
// Men 클래스 파일
public class Men {
	@Override
	public String toString() {
		return "남자입니다.";
	}
}
// Women 클래스 파일
public class Women {
	@Override
	public String toString () {
		return "여자입니다.";
	}
}
// Humen 제네릭 클래스 파일
public class Humen <T>{
	private T gender;
	
	void setGender(T gender){
		this.gender = gender;
	}
	
	T getGender(){
		return this.gender;
	}
}
// 메인 클래스 파일
public class CheckedGenderMain {
	public static void main(String[] args) {
		Humen <Men> m = new Humen<Men>();
		Humen <Women> w = new Humen<>();
		
		m.setGender(new Men());
		w.setGender(new Women());
		
		System.out.println(m.getGender());
		System.out.println(w.getGender());
		
	}
}

 

2중 제네릭 클래스 예시

더보기
// 제네릭 클래스
public class Entry <K,V> {
	private K key;  
	private V value;
	// 메인 클래스에서 Entry 클래스로 객체를 생성new Entry<String, Integer>()
	//String key;
	//Integer value;
	//이렇게 선언했다고 봐도 된다
	
	public Entry(K key, V value) {
		this.key = key;
		this.value = value;
	}
	
	public K getKey() {
		return key;
	}
	
	public V getValue() {
		return value;
	}
}
// 메인 클래스
public class EntryDemo {
	public static void main(String[] args) {
		Entry<String, Integer> e1 = new Entry<>("김선달",20);
		Entry<String, String> e2 = new Entry<>("기타", "등등");
		//Entry<int,String> e3 = new Entry<>(30, "아무개")
		
		System.out.println(e1.getKey()+" "+e1.getValue());
		System.out.println(e2.getKey()+" "+e2.getValue());
		
	}
}

 

제네릭 메소드 예시

자료형은 메소드에 들어온 매개변수의 타입으로 결정된다.

더보기
// 제네릭 메소드를 정의하는 클래스 Utils
public class Utils {
	static public <T> void showArray(T[] arr) {
		for(T x:arr) {
			System.out.printf("%s",x);
		}
		System.out.println();
	}
	
	static public <T> T getLast(T[] arr) {
		return arr[arr.length-1];
	}
}
// 메인 메소드
public class GenMethod1Demo {
	public static void main(String[] args) {
		Integer[] ia = {1,2,3,4,5};
		Character[] ca = {'a','b','c','d','e'};
		
		Utils.showArray(ia);   // 결과 12345
		Utils.<Character>showArray(ca);   // 결과 abcde
		System.out.println(Utils.<Integer>getLast(ia));   // 결과 5
		//<자료형>을 줘도 되고 안 줘도 된다.
	}
}

 

 

상속 받은 제네릭 메소드 예시 (제한된 제네릭 메소드)

상속을 이용하면 해당 타입과 자식 타입들만 매개변수로 들어올 수 있도록 제한된다.

더보기
// 제네릭 메소드가 들어 있는 클래스
public class Utils2 {
	// 제네릭 타입 T가 Number클래스를 상속받고 있다.
	// Number 클래스의 자손 타입들만 제네릭 타입 매개변수에 들어올 수 있다.
	static public <T extends Number> void showArray(T[] arr) {
		for(T x:arr) System.out.printf("%s",x);
	}
}
// 메인 클래스
public class GenMethod2Demo {
	public static void main(String[] args) {
		Integer[] ia = {1,2,3,4,5};
		Double[] da = {1.0,2.0,3.0,4.0,5.0};
		Character[] ca = {'a','b','c','d','e'};
		
		Utils2.showArray(ia);   // 결과 12345
		Utils2.showArray(da);   // 결과 1.0 2.0 3.0 4.0 5.0
		// Utils2.showArray(ca);   // 문자형은 Number 클래스의 자손 타입 형태가 아니다 
	}
}