일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- Heuristic Watermark
- Perfect Watermark
- 불공변
- apache flink
- event time
- flink watermark
- flink
- HDFS
- Perfect Watermarks
- ingestion time
- contravariant
- coroutines
- Stream
- lambda
- processing time
- Generic
- watermark
- Heuristic Watermarks
- watermarks
- kotlin
- covariant
- Coroutine
- MapReduce
- Hadoop
- flink watermarks
- 공변
- java
- Generics
- 가변성
- Today
- Total
mm Home
Java - Generics 본문
Generic
JDK 1.5에서 타입 안정성을 위해 generic 추가 됨.
//아래와 같이 raw type을 쓰게되면 ClassCastException을 발생하기 쉬웠다
List list = new ArrayList();
list.add(1);
list.add("jess");
Generic을 만들었는데, JDK 1.5 미만의 버전의 호환을 선택.
그래서…
Type Erasure
하위 호환을 위해 Type Parameter 정보를 지운다.
JDK 1.5 미만에서 컴파일이 되어도 Generic을 사용했을때와 동일하게 하기 위함이다.
List<String> list = new ArrayList<>();
//위의 코드는 Type Erasure에 의해 실제 Runtime 시에 아래와 같다.
List list = new ArrayList();
반면에, C#의 경우 구체화(Reification)로 하위 호환을 포기했다.
Wildcard
그런데.. generic하게 쓰기 위해 아래와 같으면 컴파일 되지 않는다.
public class A{}
public class B extends A {}
List<A> aList = new ArrayList<>();
List<B> bList = aList; //compile error
//List<A> 의 subType이 List<B>가 아니기 때문이다.
//ArrayList<A> <- List<A> <- Collection<A>
//ArrayList<A>의 subType은 List<A>이고, List<A>의 subType은 Collection<A> 이기 때문이다.
그래서 wildcard를 통해 타입 파라미터가 Unknown type이라고 표시해주면, 어떠한 타입이든 컴파일러가 신경쓰지 않는다.
//아래는 가능하다.
List<A> aList = new ArrayList<>();
List<?> bList = aList;
//그런데 또 아래와 같은 코드가 불가능하다.
bList.add(new Object());
// 컴파일러는 bList에 insert 해야될 타입을 몰라서 에러를 발생시킨다. (Generic 에서 타입 추론을 하기 위해 타입을 capture 하는 프로세스가 있는데, capture에 실패했기 때문에 capture of ~~ 에러가 발생)
// 컴파일러가 잘못된 타입을 넣었다고 판단하는 것.
// 아래에서 더 자세히 살펴보자.
가변성
그래서 가변성.. 가변성이란?
변할 수 있는 성질! 타입 파라미터가 변할 수 있음을 알려준다. (타입 파라미터간의 관계를 정하는 것)
기저 타입은 같지만, 타입 파라미터가 다른 경우의 두 Generic 타입을 살펴보자
C, C<A'> 가 있다. 그리고 A -> A' 의 의존성을 가지고 있다.
가변성을 주지 않으면 타입 파라미터는 기본적으로 무공변(Invariant)성을 가짐.
A -> A' 이더라도 C -> C<A'> 불가능.
공변 (Covariant)
A -> A' 일때, C -> C<A'>
*공변은 수학에서 사용하는 용어. 같은 방향으로 변한다는 뜻.
반공변 (Contravariant)
A -> A' 일때, C <- C<A'>
*불공변은 공변의 반대어. 다른 방향으로 변한다는 뜻. 위의 공변과 화살표가 반대임을 볼 수 있다.
리스코프 치환 원칙에 의하면
A = A' 가능
A' = A 불가능
그런데
공변일 때 C = C<A'>
반공변일 때 C<A'> = C
Wildcard를 통해 컴파일러가 통과하게 함. 컴파일 단계를 지나면 type erasure에 의해 raw type과 똑같으니 런타임에 문제가 없음.
왜냐면, 우리는 List<?> 를 List 처럼도 쓰고 싶고, List 처럼도 쓰고 싶을 때가 있으니까..
List<? extends A> aList = new ArrayList<>();
List<? super A> bList = new ArrayList<>();
A a = aList.get(0);
aList.add(new B()); // 안됨. B b = new A() 가 안되니까.
bList.add(new B()); // 됨. B b = new A() 를 컴파일러가 반공변으로 가능하게 했으니까. 물론 실제로 저게 가능한건 아님. 컴파일러가 타입 파라미터 추론 할때 가능하게 한 것.
Object b = bList.get(0);
먼저 알아두어야 할 것은
리스코프 치환원칙에 의해 subType으로 치환이 가능하다. (subType은 읽기 가능)
A a = b; //b 객체안에 C가 있든, D가 있든 A를 상위타입으로 가지는 어떤 것도 읽을 수 있다.
하지만 superType으로 쓰는것은 불가능하다. (superType은 쓰기 불가능)
B b = new A(); // 불가능
자기 자신의 타입은 subType이자, superType 이다.
? extends A = A
? super A = A
가 가능하다는 것.
다시 타입 관계를 알아보자.
List<?> -> List<A>
List<?> -> List<B>
List<?> -> List<? super B> -> List<? super A>, List<? extends A> -> List<A>
List<?> -> List<? extends A> -> List<? extends B>, List<? super B> -> List<B>
Type Hierarchy
앞서 이야기한듯이 파라미터 타입 A와 B사이에는 관련이 없지만, wildcard를 사용한 List의 subType이 된다.
그렇기 때문에 wildcard를 썼을때 치환이 가능했던 것.
근데 왜 공변성을 주었을 때 add 가 안될까?
List 에서 해당 없는 subType = List<? extends B>
List 에서 해당 없는 subType = List<? super A>
그러니까!!
공변으로 받으면(List <? extends A>), A extends이지만 A의 super는 아니다. -> 말이 어렵지만 집중하고 생각해보면.. A의 subType인데, A의 superType이 아니다.. A의 Super가 아니기 때문에 가장 최상위인 Object도 >아니라고 생각한다. 그래서 object를 쓸수가 없다. 대신 null은 쓸 수 있음.반공변으로 받으면(List <? super A>), A extends 이자만 A의 super는 아니다. -> A 타입이 superType이지만, A가 subType이 아니다.. A의 subType이 아니기 때문에 읽기가 불가능하다. 하지만 super 타입이기 때문에 쓰기가 가능.
in = extends (Upper bound)
out = super (Lower bound)
in variable = 사용하기 위해 코드 안으로 가져옴 (코드에 데이터 제공). consuming.
out variable = 밖에서 사용하는 데이터(다른 곳에서 사용할 데이터 보유). producing.
'개발 > Java' 카테고리의 다른 글
Java 9 features (0) | 2017.11.10 |
---|---|
Java - static (0) | 2017.09.06 |
Java 8 - Stream (0) | 2017.08.11 |
Java 8 - Lambda Translation (0) | 2017.08.10 |
Java 8 - 람다 표현식 (2) | 2017.08.10 |