라이브러리에서 또는 공식문서에서 메소드 사용법을 찾아보면 <T>
, <E>
, <?>
등등 알 수 없는 알파벳이나 물음표 같은 문자를 볼 수 있었고 타입위치에 알 수 없는 문자들을 볼 수 있었다.
먼저 제네릭은 객체,메소드 생성,사용 시에 타입(클래스)을 받아서 동적으로 결정할 수 있게 하는 기능 이다.
이 말을 줄여서 글에서 제네릭은 아무거나 담을 수 있다. 라고 표현할 수도 있다.
사실 이름은 알 수 없는 문자가 아니고 이것들이 바로 제네릭 타입이다.
T
, E
이 문자들을 제네릭 타입이라고 부르고, 어떠한 클래스가 오더라도 받아줄 수 있다.
여기서 T
, E
모두 제네릭 변수일 뿐 K
, U
등등 원하는 문자를 사용하면 되지만,
제네릭 타입 네이밍 컨벤션이 있기 때문에 사용시 찾아보고 용도에 맞게 네이밍을 하면,
자유도가 높은 제네릭을 조금이나마 추측할 수 있기에 미래의 나도 동료도 편할 거 같다.
여기서 T
, E
관련해서는 설명했지만, 아직 ?
를 설명하지 않았다.
일단 ?
는 제네릭은 아니다. 이름은 와일드카드라는 녀석이지만 와일드카드를 이해하기 위해서는
제네릭을 잘 알고 넘어가야하기 때문에 제네릭을 정리하고 와일드카드 설명해보자
제네릭은 공부하기위해서 많은 글을 찾아보면 공변과 불공변을 먼저 알아야한다는 글이 많았다.
공변, 불공변 나한테는 너무 와닿지않고 이해가 안됐고 한 줄로 요약하자면
공변 → 자식 타입의 객체가 부모 타입 업캐스팅해서 품어준다.
불공변 → 아무리 자식 타입의 객체라도 부모 타입으로 업캐스팅해서 품어주지 않는다.
이 해석을 읽는다면 틀렸는데? 아닌데? 라는 생각이 나조차 동의하고 하지만 구체적으로 표현하자니 더 이해가 안되는 어려운 말이 될 것 같아서 나는 이렇게 공변, 불공변을 정리하기로 했다.(좋은 표현이 있다면 부탁드립니다.)
제네릭을 사용하지 않고 Object를 사용할 수 있다.
왜냐하면 Object 또한 모든 객체를 파라미터로 전달할 수 있다.
제네릭과 똑같이 다 담아줄 수 있다는 장점이 있다.
하지만 Object로 강제 업캐스팅 되어 내가 사용하고 싶었던 Person 객체의 메소드를 바로 사용할 수 없다.
또한 나는 Person, User와 같은 객체 모두를 받고 싶어서 Object 타입으로 지정했는데
내가 아닌 다른 개발자가 , Integer 등 의도와 맞지 않게 사용할 수 있다.
글로 정리를 하는 것보다 그림으로 정리하는게 명확하게 이해할 수 있을거 같아서 그림 그리기
예를 들어서 빨간색이 으로 지정해뒀고 주황색이 새로 덮어쓰지 않고 사용한다면 반환 타입은
이 된다.
genericMethod(1,2)
이렇게 사용하는 경우 메소드 내부의 T
는 클래스 선언할 때 지정한 타입을 따라간다.
하지만 &lt;T>
메소드 제네릭을 활용하면 클래스 선언할 때 지정돤 타입 A와는 다른 타입B를 사용할 수 있다.
하지만 genericMethod()
를 사용할 때
메소드 제네릭 : 클래스에서 사용한 제네릭과는 독립적으로 메소드에서 사용할 타입을 메소드 사용 시 동적으로 결정할 수 있게 하는 것
제네릭은 아무 타입이나 받아주기 때문에 너무 자유로운 영혼이다.
너무 자유롭다 보니 특히 메소드를 제네릭 타입으로 받아서 사용하려고 할 때 어떤 객체를
받아서 사용할 지 예측할 수 없어 객체의 고유의 메소드를 사용하는 게 곤란해졌다.
모든 객체,클래스의 조상인 Object의 메소드 빼고는 지정할 수가 없기 떄문에 다음과 같이 사용할 수 있다.
extends
, super
를 사용해서 제네릭의 범위를 제한할 수 있다.
예제코드
class Studnet< T extends Person> {}
이렇게 extends를 사용하면 Person 클래스를 포함한 자식 타입의 클래스만 T
위치에 올 수 있다.
만약 Person의 자식 클래스가 아니라면 컴파일에러를 발생시켜 컴파일 시점에 오류를 확인할 수 있다.
다음글에서는 제네릭을 더욱 유연하고 효율적으로 사용할 수 있게하는 와일드카드를 정리해야겠다.