1. 타입파라미터와 타입 아규먼트
public class Generics {
static class Hello<T> { // T-> 타입 파라미터
T t;
T method(T val) {return null;}
}
public static void main(String[] args) {
new Hello<String>(); // 타입 아규먼트
}
}
2. 제너릭 쓰는이유
1. 컴파일시점에서 컴파일러가 정확하게 타입체킹을 할 수 있다.
public static void main(String[] args) {
List list = Arrays.asList(1, 2, 3);
list.add("runTimeError");
List<Integer> listWithGen = Arrays.asList(1, 2, 3);
listWithGen.add("compileError");
}
2. 강제 캐스팅으로 인한 오류 방지
- 제너릭을 사용한다면 컴파일러가 적절하게 알아서 타입캐스팅 해준다.
public static void main(String[] args) {
List list = Arrays.asList("string");
String s = (String)list.get(0);
}
3. Raw Type
로우타입은 제너릭이 사용안된 List, Set 등이라고 보면된다.
public static void main(String[] args) {
List<Integer> ints = Arrays.asList(1, 2, 3);
// 로우타입 (List rawInts)
List rawInts = ints;
List<Integer> ints2 = rawInts;
List<String> strs = rawInts;
// 여기서 런타임에러가 발생!
String s = strs.get(0);
}
4. static method generics
불가능
- 클래스가 생성될 때 제너릭에 대한 정보를 받아오기 때문에 static 메소드에 T 를 사용할 수는 없다. (뭐가올지 모르니까)
public class Generics<T> {
static void print(T t) {
System.out.println("t.toString() = " + t.toString());
}
}
가능
- static method 레벨에 따로 제너릭을 사용하여 사용하도록 한다.
public class Generics {
static <T> void print(T t) {
System.out.println("t.toString() = " + t.toString());
}
}
번외 : 생성자에도 제너릭 사용 가능
public class Generics {
public <S> Generics(S s) {
}
}
5. Bounded Type Parameter
타입에 대한 제한을 두겠다.
예시)
public class Generics<T extends List>{
}
아래와같이 여러개의 조건을 묶을 수 있다. (intersect)
- 클래스는 한개만 가능
- &조건만 가능 (모두 포함돼야함)
public class Generics {
static <T extends List & Serializable & Comparable & Closeable> void print(T t) {
}
}
예제)
- String 배열에서 더 큰 것의 개수를 리턴하는 함수를 만들어보자. (제너릭을 이용해서)
public class Generics {
static <T extends Comparable<T>> long countGreaterThan(T[] arr, T elem) {
return Arrays.stream(arr).filter(s -> s.compareTo(elem) > 0).count();
}
public static void main(String[] args) {
String[] strArr = new String[] {"a", "b", "c", "d", "e"};
System.out.println(countGreaterThan(strArr, "b"));
}
}
<T extends Comparable<T>>
- 크기 비교를 위해서 compareTo 를 쓴다고 가정했을 때, 넘어오는 type이 Comparable을 구현헀는지 모르므로, 타입에 제한을 둠
- Comparable 의 정보는 type erasure 가 없애지 않는다. (컴파일 이후에도 정보가 남아있다.)
// Comparable 을 서브타입으로 가지는 리스트이다.
// 타입이 정해지면 뭔지알고, 이후에 그 타입으로 사용된다.
static <T extends Comparable> void method(List<T> list) {
}
// 타입을 알 필요가 없다.
// 가져와서 Comparable 의 기능만 사용할 것이다 or List의 기능만 사용할 것이다.
static void method(List<? extends Comparable> list) {
}
public class Generics {
static void printList(List<Object> list) {
list.forEach(s -> System.out.println(s));
}
static void printList2(List<?> list) {
list.forEach(s -> System.out.println(s));
}
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3);
printList(list); // 컴파일에러
printList2(list);
}
}
printList(list); 를 하면 컴파일에러가 발생한다.
- 이유
- List<Integer> 은 List<Object> 의 서브타입이 아니기 때문
- printList(List<Object> list) 는 명확하게 List<Object> 타입의 서브타입을 원하는 것이기 때문
public static void main(String[] args) {
List<Integer> intList = new ArrayList<>();
List<Number> numberList = intList; // 컴파일에러 (List<Integer> 은 List<Number> 의 서브타입이 아님, Integer 가 Number의 서브타입일 뿐
ArrayList<Integer> arrayList = new ArrayList<>();
List<Integer> iintList = arrayList; // 가능 (ArrayList<Integer> 은 List<Integer> 의 서브타입이기 때문)
}
'토비의봄' 카테고리의 다른 글
4.2. Reactive Streams (0) | 2021.02.28 |
---|---|
4.1. Reactive Streams (0) | 2021.02.28 |
3.2. Generics (0) | 2021.02.25 |
2. 슈퍼 타입 토큰 (0) | 2021.02.20 |
1. 재사용성과 다이나믹 디스패치, 더블 디스패치 (0) | 2021.02.20 |