JAVA

자바 제네릭(Generic)

커피마시기 2023. 10. 16. 19:28

 

 

 

 

Generic 이란

 

자바에서 제네릭(Generic)은 클래스 내부에서 사용할 데이터 타입을 외부에서 사용자에 의해 지정하는것을 의미하는것으로 객체별로 다른 타입의 자료가 저장될 수 있도록 한다

List<String> list = new ArrayList<>();
  • 해당 코드에서 <> 꺾쇠 괄호가 제네릭이다, 리스트 클래스 자료형의 타입을 String 타입으로 지정되어 문자열 데이터만 List에 담을 수 있게된다
  • 즉 변수를 선언할 때 변수의 타입을 지정하듯이 제네릭은 객체에 타입을 지정해주는 것이라고 생각하면 된다

 


 

	// 제네릭 타입 매개변수에 정수 타입 할당
	List<Integer> list = new ArrayList<>();
	
	// 제네릭 타입 매개변수에 문자열 타입 할당
	List<String> list = new ArrayList<>();
	
	// 제네릭 타입 매개변수에 실수 타입 할당
	List<Double> list = new ArrayList<>();
	
	// 제네릭 타입 매개변수에 클래스(Soccer class가 있다고 가정) 타입 할당
	List<Soccer> list = new ArrayList<>();
    
    
    
    public class Note4<Soccer> {
	
	List<Soccer> list = new ArrayList<>();
	
	public void add(Soccer player) {
		list.add(player);
	}
}
  • List<T>의 <T>는 타입 매개변수라고 부르며 제네릭을 이용한 클래스나 메소드를 설계할 때 사용된다.
  • new 생성자 부분의 제네릭 타입을 생략 할 수 있다
  • 제네릭에서 할당 받을 수 있는 타입은 Reference 타입 뿐이다 int, double과 같은 타입은 제네릭 타입 파라미터로 넘길 수 없다

 


 

class Soccer {}

class Tottenham extends Soccer {}

class ManCity extends Soccer {}

class Note4<T> {
	
	List<T> player = new ArrayList<>();
	
	public void add(T info) {
		player.add(info);
	}
}

public class Main {
	public static void main(String[] args) {
		
		Note4<Soccer> list = new Note4<>();
		
		// 제네릭 타입의 다형성 원리 적용
		list.add(new ManCity());
		list.add(new Tottenham());
	}
}
  • 제네릭 타입 파라미터에 클래스 타입으로 온다는 것은 객체지향 프로그래밍의 다형성 원리가 그대로 적용된다.
  • 복수로 타입을 사용할 수도 있으며 클래스 초기화할 때 제네릭 타입을 두개 넘겨주어야한다

 

 


 

 

Generic 사용 이유

1.  컴파일 타임에 타입 검사를 통해 미리 예외 방지

2.  불필요한 캐스팅을 없게하여 형 변환의 번거로움을 줄이고 가독성이 좋아진다

 

 

 Generic 사용시 주의사항

 

1.  제네릭 타입의 객체는 생성 불가

class Note4<T> {
	
	public void Practice() {
		
		T t = new T();
	}
}

 

2.  static 데이터 타입에 제네릭 타입이 올 수 없다

 

class Note4<T> {
	
	private String team;
	private int num;
	
    // static 메소드의 매개변수 타입으로 사용 x
	public static void Practice(T n) {
		
	}
}

static은 클래스가 동일하게 공유하는 변수로서 제네릭 객체가 생성되기 전에 이미 자료 타입이 정해져 있어야 하기 때문이다

 

 

 

 

3.  제네릭으로 배열 선언

기본적으로 제네릭 클래스 자체를 배열로 만들 수 없다, 하지만 제네릭 타입의 배열 선언은 허용된다

// 잘못된 예
class Note4<T> {}

public class Main {
	public static void main(String[] args) {
		
		Note4<Integer>[] arr1 = new Note4<>[5];  //  x
	}
} 

// 올바른 예
class Note4<T> {}

public class Main {
	public static void main(String[] args) {
		
		Note4<Integer>[] arr1 = new Note4[5];
		
		arr1[0] = new Note4<Integer>(); // 위에서 이미 정의했기 때문에 Integer 사용가능
		
		arr1[1] = new Note4<String>(); // Integer가 아닌 타입은 저장 불가
	}
}

 


 

제네릭 활용해보기

 

● 제네릭 클래스

public class Note4<T> {
	
	private T val;
	
	
	// T 타입의 team 반환
	public T getVal() {
		return val;
	}
	
	public void setVal(T val) {
		this.val = val;
	}
}
public class Main {

	public static void main(String[] args) throws Exception {
		
		Note4<Integer> n1 = new Note4<>();
		
		n1.setVal(1);
		
		Note4<String> n2 = new Note4<>();
		
		n2.setVal("문자열 제네릭");
	}
}

 

 

● 제네릭 인터페이스

인터페이스에서도 제네릭을 적용 할 수 있으며 인터페이스를 implements 해준 클래스에서도 오버라이딩한 메소드를 제네릭 타입에 맞추어 구현해주어야한다

interface Inter<T>{
	public void addVal(T t, int num);
	public T getVal(int num);
}

class Note5<T> implements Inter<T> {
	
	private T[] arr;
	
	public Note5() {
		arr = (T[]) new Object[5];
	}

	@Override
	public void addVal(T t, int num) {
		arr[num] = t;
	}

	@Override
	public T getVal(int num) {
		return arr[num];
	}

	@Override
	public String toString() {
		return "Note5 [arr=" + Arrays.toString(arr) + "]";
	}
	
}
public class Main {

	public static void main(String[] args) throws Exception {
		
		Note5<String> note = new Note5<>();
		
		note.addVal("인터페이스에서 활용", 4);
		note.getVal(4);
		
		System.out.println(note.toString());
	}
}

출력 결과

 

 


 

 

 

 제네릭 타입 범위 한정

  • 제네릭에 타입을 지정해줌으로서 예외에 대한 안정성을 확보하는 것은 좋지만 너무 자유롭다는 점이 있다
  • 예를들어 계산기 클래스가 있는 경우 정수, 실수 구분없이 받을 수 있도록 제네릭으로 클래스를 만들어 주었을때 숫자 관련클래스 뿐만 아니라 String이나 다른 클래스도 대입이 가능하다는 문제가 있다
  • 이로 인해 나온 것이 제한된 타입 매개변수<T extends [제한타입]> 이다, 다만 상속 키워드와 같지만 <> 안에 extends가 있으면 제한을 의미하고 괄호 바깥에 있으면 상속으로 볼 수 있다
class Calculator<T extends Number> {
	void add(T a, T b) {}
	void min(T a, T b) {}
}

public class Main {

	public static void main(String[] args) {
		
		// Number 클래스만 받도록 제한
		Calculator<Number> cal1 =  new Calculator<>();
		
		Calculator<Integer> cal2 =  new Calculator<>();
		Calculator<Double> cal3 =  new Calculator<>();
		
        // Number 이외 오류
		Calculator<String> cal4 =  new Calculator<>();
	}
}

 

 

 


 

 

Today short review

 

수업을 듣던 와중 강사님께서 Generic에 대한 질문을 던졌을때 더 공부해야겠다는 생각이 들어 제네릭에 대해 조금 더 공부 해보았습니다. https://inpa.tistory.com/ 님이 작성 하신 글이 정리가 잘되어 있어 많이 배웠고 인터페이스를 활용한 부분 & 타입 제한에 대해 알 수 있었습니다

'JAVA' 카테고리의 다른 글

자바 JDK 설치 및 환경변수 설정  (0) 2023.10.23
자바 JDBC  (0) 2023.10.12
자바 static  (0) 2023.10.10
자바 인터페이스(Interface)  (0) 2023.10.06
자바 상속 extends  (0) 2023.09.29