-
Generics (제네릭스)고급 JAVA/Generics 2020. 3. 21. 21:13
Generics (제네릭스)
해당 클래스나 메소드가 다룰 객체를 미리 명시해줌으로써 형변환을 하지 않고 사용하는 것
사용하는 이유
- 타입의 안정성 제공
- 타입체크와 형변환을 생략
- 클래스와 인터페이스, 메소드를 정의할 때 타입(Type)을 파라미터(Parameter)로 사용할 수 있도록 함
장점
1. 컴파일 시 강한 타입 체크를 할 수 있다.
- 실행 시 타입 에러가 나는 것보다 컴파일 시에 미리 타입을 강하게 체크해서 에러를 사전에 방지
2. 타입 변환(Castring)을 제거
- 비제네릭 코드는 불필요하게 타입 변환을 하기 때문에 프로그램 성능에 악양향을 미침
List list = new ArrayList(); list.add("hello") String str = (String) list.get(0); // type casting 필요 ----------------------------------------------------- List<String> list2 = new ArrayList<>(); list2.add("hello"); String str2 = list.get(0); // type casting 불필요
제네릭 타입(class, interface)
제네릭 타입은 타입을 파라미터로 가지는 클래스와 인터페이스를 의미
클래스 또는 인터페이스 이름 뒤에 <> 부호가 붙고, 사이에 타입 파라미터가 위치한다.
public class 클래스<T> { ... } public interface 인터페이스<T>{ ... }
* 타입파라미터는 일반적으로 대문자 알파벳 한 글자로 표현
제네릭 타입과 비제네릭 타입 비교
// 비제네릭 타입 publci class Corn{ private Object obj; public Object get(){ retunr obj; } public void set(Object obj){ this.obj = obj; } } // 제네릭 타입 publci class Corn<T>{ private T t; public T get(){ retunr t; } public void set(T t){ this.t = t; } }
* Object 타입을 사용하면 모든 객체를 저장할 수 있는 장점이 있지만, 저장할 때 타입 변환이 발생하고
읽어 올때에도 타입 변환이 발생한다.
public class Example{ // 비제네릭 Corn cron = new Corn(); corn.set("콘"); // String -> Object (자동 타입 변환) String name = (String) corn.get(); // Object -> String (강제 타입 변환) // 제네릭 Corn<String> corn = new Corn(); cron.set("콘"); String name = corn.get(); }
멀티 타입 파라미터(class <K, V...>, interface<K, V...>)
제네릭 타입은 두 개 이상의 멀티 파라미터를 이용할 수 있다.
이 경우 각 타입 파라미터는 콤마로 구분한다.
Entry 인터페이스 구현
public class Entry implements Map.Entry<K, V>{ private K key; private V value; public Entry(K key, V value){ this.key = key; this.value = value; } public K getKey(){ return this.key; } public V getValue(){ return this.value; } }
제네릭 메소드
제네릭 메소드는 매개타입과 리턴타입으로 타입파라미터를 갖는 메소드를 말한다.
제네릭 메소드 선언 방법
리턴 타입 앞에 <> 기호를 추가하고 타입 파라미터를 기술한 다음, 리턴 타입과 매개 타입으로
타입 파라미터를 사용하면 됨
public <타입파라미터, ...> 리턴타입 메소드명(매개변수,...){...} public <T> Corn<T> makePopCorn(T t){...} // 제네릭 메소드 호출 방법 Corn<Integer> = <Integer>makePopCorn(10); // 명시적으로 Interger를 지정 Corn<Integer> = makePopCorn(10); // Integer로 추정 (컴파일러가 매개 값을 보고 타입 추정)
제한된 타입 파라미터
<T extends 최상위 타입>
타입 파라미터에 지정되는 구체적인 타입은 제한될 필요가 있다.
예를 들어, 숫자를 연산하는 제네릭 메소드는 매개값으로 Number 타입 또는
하위 클래스 타입(Byte, Integer, Long, Double)의 인스턴스만 가져야 한다.
이것이 제한된 타입 파라미터가 필요한 이유이다.
선언 방법
타입 파라미터 뒤에 extends 키워드를 붙위고 상위 타입을 명시한다.
* 상위 타입은 클래스뿐 아니라 인터페이스도 가능 (그러나 인터페이스라도 implements가 아닌 extends를 사용한다.)
public <T extends Number> int compare(T t1, T t2){ double v1 = t1.doubleValue(); }
와일드 카드 타입
- <?> : 모든 클래스나 인터페이스 타입이 올 수 있다.
- <? extends ..> : 상위 클래스 제한
- <? super> : 하위 클래스 제한