728x90
기본 - 제네릭 클래스 => 무공변성 -> 즉 공변이 없다
out -> 공변성 => 자기자신과 자식 객체 허용 -> 생산만 -> 꺼낼수만 있다.
in -> 반공변성 => 자기자신과 부모 객체 허용 -> 소비만 -> 넣을수만 있다.
*변성 -> 제네릭 클래스 타입 파라미터에 따라 제네릭 클래스 간의 상속 관계가 어떻게 되는지를 나타내는 용어
선언 지점 변성 -> 클래스 자ㅔ를 공변하거나 반공변하게 만드는 방법
사용 지점 변셩 -> 특정 함수 또는 특정 변수에 대해 공변/반공변을 만드는 방법
제네릭 제약: class Cage<T: Animal> 처럼 제네릭 클래스의 타입 파라미터의 제약을 걸 수 있다. 그외로는 where 로 여러 제약을 줄 수 있다.
타입소거: JDK 의 호환성을 위해 런타임 때 제네릭 클래스의 타입 파라미터 정보가 지워지는 것
- 타입 파라미터 끼리, 상속관계가 존재하더라도 실제 제네릭 클래스의 코드에서는 아무 관계가 아니다
- 자바의 배열과 리스트를 살펴보자
- 자바의 배열은 제네릭과 다르다
A 객체가 B 객체의 하위 타입이면,A 배열이 B 배열의 하위 타입으로 간주한다.- Object(상위클래스) <- String(하위클래스) => Object[] <- String[]
- 이러한 관계를 공변하다고 한다.(상속관계가 유지되므로..)
- 공변하기 때문에 아래와 같이 이상한 코드가 작성 가능하다.
- 자바의 배열은 제네릭과 다르다
String[] strs = new String[]{"a", "b", "c"}; // 가능
Object[] objs = strs; // 가능
Object[0] = 1; // 가능은 하지만 문제가되는 부분
-> objs 사실 String[] 이기 때문에 int 를 넣을 수 없다. 런타임 에러 발생 함.
- List 는 제네릭을 사용하기 때문에 공변인 Array 와 다르게 무공변 하다
List<String> strs = List.of("a", "b", "c");
List<Object> objs = strs; // Type Mismatch! 발생 즉 컴파일 때 발견
- 위와같은 이유에서 이펙티브 자바에서는 -> Item28. 배열보다는 리스트를 사용하라 라고 적혀있음.
- 제네릭은 자바1.5(자바 5) 부터 개념이 생김
- 제네릭이 없던 시절의 List 와 제네릭이 생긴 후 List<String> 호환성을 유지해야 한다.
- 이러한 호환성을 유지하기 위해 Java 5 는 List<String> 도 runtime 떄는 type 정보를 제거하는 방식을 선택함.
- 이로인해 Java 에서는 지금도 raw type 을 만들 수 있다
- raw type: 제네릭 타입을 사용하지 않은 타입을 말한다. 즉 타입을 지정하지 않는..
ex: val list = listOf(1, 2, 3)
- raw type: 제네릭 타입을 사용하지 않은 타입을 말한다. 즉 타입을 지정하지 않는..
- 코틀린 같은 경우에는 언어 초기부터 제네릭이 고려되었기 때문에 ray type 객체를 만들 수 없다
// raw type 이란 제네릭 타입을 사용하지 않은 타입을 말한다.
// 절대 권장하지 않음
val list = listOf(1, 2, 3)
// 코틀린 같은 경우에는 언어 초기부터 제네릭이 고려되었기 때문에 ray type 객체를 만들 수 없다
// One type argument expected for interface List<out E>
// val list2: List = listOf(1, 2, 3)
- 여튼 코틀린도 JVM 위에서 동작하기 때문에 런타임 때는 타입 정보가 사라진다 이를 타입 소거라 부른다.
// 타입 소거를 확인할 수 있는 코드
fun checkStringList(data: Any) {
// Cannot check for instance of erased type: List<String>
// if (data is List<String>) { 에러..
// }
}
// 런타임 때는 String 정보가 사라지기에 List<string> 인지 알 수 없다.
- star projection 을 활용해 최소한 List 인지는 확인할 수 있다.
if(data is List<*>) {
println("List<String> 타입이 맞습니다.")
}
// 때때로 제네릭 타입 파라미터까지 추측할 수도 있다.
fun checkMutableList(data: Collection<String>) {
if (data is MutableList<String>) {
println("MutableList<String> 타입이 맞습니다.")
}
}
728x90
'공부 > 이펙티브코틀린' 카테고리의 다른 글
아이템 27 - 변화로부터 코드를 보호하려면 추상화를 사용하라 (0) | 2024.06.21 |
---|---|
아이템 26 - 함수 내부의 추상화 레벨을 통일하라. (0) | 2024.06.21 |
아이템 23 - 타입 파라미터의 섀도잉을 피하라 (0) | 2024.06.13 |
아이템 22 - 일반적인 알고리즘을 구현할 때 제네릭을 사용하라 (0) | 2024.06.13 |
아이템 21 - 일반적인 프로퍼티 패턴은 프로퍼티 위임으로 만들어라 (0) | 2024.06.06 |
댓글