ref :
https://st-lab.tistory.com/153
https://yaboong.github.io/java/2019/01/19/java-generics-1/
제네릭스
는다양한 타입의 객체
들을 다루는메서드
나컬렉션 클래스
에컴파일 시의 타입체크
를 해주는 기능이다
-자바의 정석
- 제네릭(Generic)은
클래스 내부에서 사용할 데이터 타입
을외부에서 지정
하는 기법을 의미
-생활코딩
제네릭
은다양한 버그
를컴파일 타임에 발견
할 수 있도록코드에 안정성
을 더한다
-Oracle Javadoc
- 정해져 있지는 않지만,
일반적
으로위의 의미
로 사용- 타입으로는 반드시 참조 타입(
Reference Type
)만 올 수 있음
(primitive type
은불가능
)
기본 선언
public class ClassName <T> { ... } public Interface InterfaceName <T> { ... }
제네릭 클래스 사용
<Type>
을 통해서 직접타입(Type)
을 명시해주어야 한다public class ClassName <T, K> { ... } public class Main { public static void main(String[] args) { ClassName<String, Integer> a = new ClassName<String, Integer>(); } }
메소드
의선언 부에 적은 제네릭
으로,리턴타입
과파라미터 타입
이정해지는 메소드
반환타입
/파라미터
하나
가제네릭
이라고제네릭 메소드가 아님
컴파일러
에게타입을 미리 알려주기 때문
에static 으로 선언하여 사용 가능
제네릭 메소드
의타입 파라미터(Type Parameter)
는제네릭 클래스
와별도로 취급
:제네릭 클래스
의<T>
를 사용하고,제네릭 메서드
에<T>
를 사용해도서로 다름!
반환(return) 타입
앞
에제네릭에 대한 선언
컴파일러
에게제네릭 메서드
라는 것을알려주기 위해
리턴타입을 정의하기 전
에제네릭 타입에 대한 정의
를 반드시 적어야 함
명시적으로 호출
할 때타입을 지정
하는 방법 -->권장
타입을 지정하지 않고
,컴파일러에게 추론
하도록 하는 방법public class Util { public static <T> Box<T> boxing(T t) { Box<T> box = new Box<T>(); box.setT(t); return box; } } public class Main { public static void main(String[] args) { /* 명시적 호출 */ Box<Integer> box1 = Util.<Integer>boxing(100); /* 컴파일러 추론 호출 */ Box<String> box2 = Util.boxing("암묵적호출"); } }
제네릭으로 사용
될타입 파라미터
의범위
를한정적으로 제한
하는 방법- 한정적으로 범위를 좁히는데
3가지 방법
이 존재
extends
super
?
(와일드 카드
)
- 표기
<? extends T>
- 설명
T
와T의 자손 타입
만가능
상한 경계
라고 부름인터페이스
나클래스
나추상클래스
나 모두extends 키워드
사용
- 예시
/* T는 Comparable 인터페이스의 서브클래스들로만 정의할 수 있도록 제한 */ public class GenericArrayList<T extends Comparable<T>> // Comparable를 구현한 클래스들은 비교 가능하게 됨
- 표기
<? super T>
- 설명
T
와T의 부모(조상) 타입
만가능
하한 경계
라고 부름인터페이스
나클래스
나추상클래스
나 모두super 키워드
사용
- 예시
/* T는 Number의 상위 클래스만 타입으로 가지도록 제한 */ public class MyClass<T super Number>
- 표기
<?>
- 설명
<? extends Object>
와의미가 동일
'unbounded wildcard'
라고 부름?
자리에는모든 참조 타입(reference type)
이 올 수 있다
- 주 사용 용도
Object Class에서 지원하는 기능
을사용
하는메서드
를구현
할 때
- 매개변수로
List<T>
와List<Object>
는 다르게 동작List<T>
:Object
와Object를 상속한 모든 클래스 객체
를대신 가능
List<Object>
:Object 타입
의객체만 가지는 List
- 즉,
제네릭
에서는상속관계가 적용되지 않아서
Object만
받을 수 있게 됨
(만약List<Object>
인데List<Integer>
호출하면 오류가 발생!!)Type Parameter
를 신경쓰지 않아도 되는메서드를 구현
할 때
특정 타입이 정해져 있지 않기 때문
에 반드시readOnly 로 사용
해야 함특정 Type Parameter
를 신경쓰지 않아도 즉,변경하지 않고 읽기만
하면사용 가능
- 예시
/* T는 모든 참조 타입이 올 수 있음 */ public class MyClass(List<?> list){ ... }
new 키워드
에사용 불가능
new 연산자
는heap 영역
에충분한 공간
이 있는지 확인 후할당
을 함충분한 공간을 알기 위해서
는타입(Type)
을알아야 한다
컴파일 시점
에new T[5]
같은 예시는타입을 알 수 없으니
객체를 생성할 수 없다!
static 변수
에 사용 불가능
static 변수
는인스턴스에 종속되지 않는 클래스 변수
로,모든 인스턴스
가공통된 저장공간을 공유
하는 변수(값 자체가 공유됨
)하나로 공유되는 공유변수
가생성되는 인스턴스에 따라 타입이 바뀐다는 것
자체가말이 안됨
static 메서드
는사용 가능
- 불필요한 타입 변환 최소화
제네릭
은 클래스를 설계할 때구체적인 타입을 명시하지 않고
,타입 파라미터(T)로 대체
했다가실제 클래스를 사용
할 때구체적인 타입(String, Integer 등)
을지정함
으로써타입 변환
을최소화
- 타입 안정성 보장 (with.
컴파일 오류
)
컴파일 시점
에타입을 검사
해서오류를 발생
시킨다런타임 오류
가 아닌,컴파일 오류
를발생
시키는 것은개발자에게 행복한 일
ㅠ
- 코드의
재사용성 향상