제네릭스 쉽게 이해 하기

반응형
반응형

자바를 공부하면서 어렵다고 느끼는게 뭐냐고 물어보면

가장 먼저 제네릭을 떠올릴 것입니다. 물론 람다도 있긴하지만 

람다 같은 경우는 몰라도 API를 읽는데는 그리 문제가 되지 않죠. 왜냐하면 람다가 아닌 버전도 알려주기 때문입니다.

람다를 사용하는 이유가 함수형 처럼 사용하기 위해서인데 자바는 함수형 언어가 아니기 때문이죠..

(하지만 람다도 반드시 알아야 하는 것 중 하나죠)

 

하지만 제네릭 같은 경우는 다릅니다. 이걸 모르면 API를 읽는데 어려움이 있죠.

근데 제네릭이라는게 쉽게 와닿지가 않습니다. 왜냐하면 이걸 굳이 써야 하는지 의문이 들때가 많기 때문이죠.

그래서 곰곰히 생각했습니다.

 

어떻게 하면 제네릭을 더 잘 이해할 수 있을까?

 

일단 제네릭에 대한 뜻을 알아 봅시다.

 

네 일반적이라는 뜻입니다. 그런데 이게 프로그래밍과 도대체 뭔 관련이 있을까요?

 

일반적으로 제네릭은 형변환을 강제하기 위해 만들어진거라고 알고 있습니다.

이건 알겠는데... 제네릭이 들어온 클래스는 도대체 어디서 온것이며... 이것들은 도대체 어떻게 사용하는지 감이 잡히지 않더라구요

 

네, 제네릭을 사용하면 강력하게 프로그래밍 할 수 있다는 거 알고 있습니다.

근데... 왜 강력할까요? 물론 그 값만 받아오기 때문에 강력한거 맞습니다. 

그러면 제네릭 입장에서는 아니 메소드 클래스 입장에서는 제네릭안에 있는 클래스?를 어떻게 판단내릴까요?

 

 

보통 메소드나 클래스를 생각하면 상자라는 이미지가 생각납니다.

여기에 제네릭에 감미된건 아마 특정 무언가만 받아주는 상자정도라고 만 생각하면 될 것 같습니다.

 

그래요.. 여기까지 이해했습니다. 근데 여기서 중요한 녀석이 등장합니다. 바로 와일드 카드 ? 라는 친구죠.

 

<?>라고 사용하면 모든 타입이 가능하다고 합니다.

그러면 <T>는? 도대체 이거랑 뭐가 다를까요?

 

결론부터 말씀드리자면 제네릭에 대한 관점에서 볼때는 다른점이 하나도 없습니다.

다만 이 둘의 차이점은 제네릭 자체가 아니라 메소드,클래스 안에서 나타납니다.

 

	static public void start(List<?> list) {
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
	}
	
	static public <T> void second(List<T> list) {
		for(T t : list) {
			System.out.println(t);
		}
	}

 

바로 list에 대한 값을 알려줍니다. 자세히 보시면 와일드 카드를 사용한 부분은 일반 for문이 사용되었고,

T를 사용하는 부분은 향상된 for(for-each)문이 사용되었다는것을 알 수 있습니다.

이점이 다른점입니다.

만약 위와 같이 반복문을 사용한다면 후자의 방법이 더 좋겠지만...

굳이 반복문을 사용하지 않는다면 전자인 와일드 카드 방식이 더 좋을 것 같습니다. 왜냐하면 <?>만 사용하면 편하기 때문이죠.. 

 

추가 적으로 public뒤에  <T>대신 와일드 카드는 사용할 수 없으며

클래스에도 와일드카드를 사용할 수 없었습니다.

단지, 파라미터에서만 사용할수 있는 귀중한 녀석입니다.

 

두개를 같이 사용할 수 있을까요? 해보지는 않았지만, 아마 사용 가능할 겁니다.

확인 결과 사용이 되는것으로 확인되었습니다.

 

근데 제네릭이 달랑 ? 또는 T같이 하나의 문자로만 이뤄졌을까요?

아닙니다. 바로 extends와 super이라는 키워드를 사용할 수 있습니다. 와일드 카드 같은 경우를 먼저 보시면

	static public void start(List<? extends String> list) {
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
	}
    
	static public void start(List<? super String> list) {
		for (int i = 0; i < list.size(); i++) {
			System.out.println(list.get(i));
		}
	}

 

extends부터 설명하겠습니다. 보통 extends는 상속에 사용되는 키워드입니다. 그렇기 때문에 이것도 상속과 관련이 있겠죠? 네 맞습니다. 여기에서는 String으로 상속 받은 녀석들만 와일드 카드로 받아집니다.

super이요? 그냥 반대입니다. 

 

즉, extends는 String과 하위만 가능하고.

    super은 String과 상위만 가능합니다. (여기에서는 스트링을 사용했지만 이건 이해도를 높이기 위해서일뿐 T를 사용해도 무방합니다.)

참고로 extends당한 겨우는 향상된 for문을 사용하는게 가능해집니다.

왜냐하면 String에 대한 하위 타입이 등장했기 때문이죠. 하지만 직접적으로 ? 는 사용이 불가능합니다.

 

그러면 와이드 카드가 아닌 T를 사용하면 어떨까요?

	static public <T extends String> void second(List<T> list) {
		for(T t : list) {
			System.out.println(t);
		}
	}

바로 이렇게 됩니다. 특이한점은 와일드 카드 같은 경우는 매개변수에 사용이 가능했지만...

사용하지 않는 경우에는 매개변수에 사용이 불가합니다.

 

 

자 이제 정리 하겠습니다.

제네릭은 형타입을 강제하기 위해 사용된다.

제네릭을 메소드에 사용하는 방법은 2가지인데 

첫 번째 방법은 와일드 카드(?)를 이용하는 방법이고,

두 번쨰 방법은 클래스와 똑같이 <T>를 이용하는 방법이다.

다만 <T>라고만 작성하면 메소드 입장에서  모르기 때문에 명시해줘야 한다!

 

그리고 extends(하위 부터 대상자까지),super(대상자부터 상위까지) 지원된다.

 

 

반응형

'프로그래밍 언어 > 자바' 카테고리의 다른 글

서블릿과 JSP  (0) 2020.09.24
public , default, protected, private  (0) 2020.07.30
내가 쓰레드를 이해한 방법  (0) 2020.06.24
추상화란 무엇일까?  (0) 2020.06.17
얕은 복사와 깊은복사는 무엇일까?  (0) 2020.06.08

댓글

Designed by JB FACTORY