개발/Spring

자바 Java 제네릭 Generic

728x90

JDK 1.5에 처음 도입됨

타입 형 변환에서 발생할 수 있는 문제점을 사전에 없애기 위해서.

 

자바에서는 여러 타입들이 존재하기 때문에 형 변환을 하면 예외가 발생할 수 있다.

 

클래스 내부에서 사용할 데이터 타입을 외부에서 지정하는 기법

다양한 타입을 다루는 객체들을 다루는 메소드나 컬렉션 클래스에서 컴파일 시의 타입 체크를 해주는 기능

 

타입 형 변환에서 발생할 수 있는 문제점을 사전에 없애기 위해 만들어 졌음.

 

여기서 사전은 컴파일 할 떄 점검할 수 있다는 의미이다.

컴파일 시점에서 잘못된 타입인 것을 알기 위해서

 

 

제너릭을 사용하지 않을 경우에 발생하는 문제점 :

 

제너릭 타입 이름 정하기

 

 

제네릭 타입을 선언할 때에는 클래스 선언시 꺽쇠에 들어가는 기본 규칙이 있다.

 

  • E : 요소
  • K : 키
  • N : 숫자
  • T : 타입
  • V : 값
  • S, U, V: 두번째, 세번째, 네번째에 선언된 타입

이 규칙을 지켜야 컴파일 되는 것이 아니다. 하지만 다른 어떤사람이 보더라도 쉽게 이해할 수 있도록 하려면 이 규칙을 따르는 것이 좋다

 

특히 E는 자바 컬렉션에서 많이 사용되고 있다.

 

 

 

제레닉의 ?의 의미

 

제네릭을 사용할 때 <>안에 들어가는 타입은 기본적으로 어떤 타입도 상관 없다.

 

제네릭한 클래스의 타입만 바꾼 다고 해서 오버로딩이 불가능 하다. 그래서 이런 경우에는

wildcard(WildcardGeneric<?> c) 이렇게 선언이 가능하다.

 

타입에 ?를 적어주면 어떤 타입이 제네릭 타입이 되어도 상관없다. 하지만 메소드 내부에서는 타입을 정확히 모르기 때문에 앞서 사용한 것처럼 String으로 앞서 사용한 것처럼 String으로 값을 받을 수 없고, Object로 처리해야만 한다. 이 불편함을 해경하기 위해서 wildcard가 등장했다.

여기서 ? 라고 명시한 타입을 영어로는 wildcard 타입이라고 부른다.

 

와일드 카드 : 제네릭 클래스의 객체를 메소드의 매개변수로 받을 때 그 객체 타입변수를 제한하는 것을 말한다.

 

만약에 넘어오는 타입이 두세 가지로 정해져 있다면, 다음과 같이 메소드 내에서 instanceof 예약어를 사용하여 해당 타입을 확인하면 된다.

 

그리고 wildard는 메소드의 매개 변수로만 사용하는 것이 좋다.

WildcardGeneric<?> wildcard = new WildcardGeneric<String>();

이 코드에서 에러가 발생한다 .

 

알수 없는 타입에 String을 지정할 수 없다. 즉 어떤 객체를 wildcard를 선언하고, 그 객체의 값을 가져올 수는 있짐나, 와일드 카드로 객체를 선언했을 때에는 이 예제와 같이 특정 타입으로 값을 지정하는 것은 불가능하다.

(wildcard : 특정 명령어로 명령을 내릴 때, 여러 파일을 한꺼번에 지정할 목적으로 사용하는 기호)

 

 

제네릭 선언에 사용하는 타입의 범위도 지정할 수 있다.

 

public void boundedWildcardMEthod(WildcardGeneric<? extends Car> c

 

?라는 wildcard는 어떤 타입이 오더라도 상관이 없었다. 하지만 ? extends Car 라고 적어 주면 Car로 상속받은 모든 클래스를 사용할 수 있다는 의미이다.

 

매개변수에는 다른 타입을 제네릭 타입으로 선언한 객체가 넘어올 수 없다.

 

컴파일 시 에러가 발생하므로 Car 클래스와 관련되어 있는 상속한 클래스가 넘어와야만 한다.

 

 

? extends 타입 같은 것을 Bounded Wildcards라고 부른다.

Bound = 경계, 매개변수로 넘어오는 제네릭 타입의 경계를 지정하는데 사용한다는 의미로 해석하면 된다.

 

앞서 말한대로 Bounded Wildcards로 선언한 타입에는 값을 할당할 수 없다.

 

 

 

 

메소드를 제네릭하게 선언하기

 

앞선 방법의 큰 단점은 매개 변수로 사용된 객체의 값을 추가할 수 가 없다.

 

그래서 아래와 같이 선언해서 사용할 수 있다.

 

public <T> void genericMethod(WildcardGeneric<T> c, T addValue)

 

리턴타입도 제네릭을 선언하고 제네릭 타입이 포함된 객체를 받아서 처리하였다.

이처럼 메소드 선언시 리턴타입 앞에 제네릭한 타입을 선언해주고, 그 타입을 매개변수에서 사용하면 컴파일 된다.

 

그리고 명시적으로 메소드를 선언시 타입을 지정해주면 보다 아래와 같이 더 견고한 코드를 작성할 수 있다.

 

public <T extends Car> void genericMethod(WildcardGeneric<T> c, T addValue)

 

 

제네릭한 타입이 두개인 경우,

 

public <S, T extends Car> void mutiGenericMethod(WildcardGeneric<T> c, T addValue, S another)

 

제네릭한 클래스를 선언해서 사용할 일은 그렇게 많지 않겠지만 분명히 제네릭한 클래스를 사용할 일은 매우 많을 것이다.

 

 

요약 : 

제네릭이 자바에 추가된 이유? : 자료형 별로 메소드를 만들면 코드의 효율성이 떨어진다. 

 

그래서 Object 세팅을 하게 되면 변환  Object 되기 때문에 반드시 형변환을 해야 하는 번거로움 생긴다. 

 

이럴때 사용시점에 자료형을 지정하는 제네릭 방식을 사용한다.

 

자바의 컬렉션프레임워크에는 어떤 자료형이라도 담을 있다.

하지만 사용하다 보니 반복문으로 동일 자료형의 연산을 하거나,

클래스의 메소드를 호출하는 것이 쉽지 않다.(instanceof 확인 casting 해야 하는 불편함) 그래서 컬렉션 프레임워크(set, List, Map)에는 담을 자료형을 한가지로 한정해서 담는 것이 좋다.

 

 

 

 

'개발 > Spring' 카테고리의 다른 글

아파치 톰캣  (0) 2021.03.07
Java 8의 새로운 것들, 변경된 것들.  (0) 2021.03.07
자바 I/O  (0) 2021.03.05
가비지 컬렉터  (0) 2021.03.01
java.lang 패키지  (0) 2021.02.28