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가지 방법이 존재
extendssuper?(와일드 카드)
- 표기
<? 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.
컴파일 오류)
컴파일 시점에타입을 검사해서오류를 발생시킨다런타임 오류가 아닌,컴파일 오류를발생시키는 것은개발자에게 행복한 일ㅠ
- 코드의
재사용성 향상