NPE
533
2016-11-10 01:01:34 작성 2016-11-10 01:03:10 수정됨
2
1001

Collections<? super T>, Collections<? extends T> 질문입니다.


Generic 쓰다가 뭔가 이상해서 살펴보니까

Collections 에서는 Generic이 이상하게 동작해서 질문드립니다.

그다지 생각안하고 사용했는 데, 스스로 부끄럽네요.


Person  ← Worker  ← EmartWorker 상속 구조일 때,

package ntest;

import java.util.ArrayList;

public class Main {
	public static void main(String[] args) {
		/* 일반적인 함수호출은 정상적 */
		Info<Worker> personInfo = new Info<>();
		test1(personInfo); // 자기 자신의 Type이므로 허용.
		test2(personInfo); // 자기 자신의 Type이므로 허용.
		
		
		/* 상한 */
		ArrayList<? super Worker> list1 = new ArrayList<>();
		list1.add(new Person()); // ??? 상한으로 허용되어야 하는 데 Error!
		list1.add(new Worker()); // 자기 자신으므로 허용.
		list1.add(new EmartWorker()); // ??? 상한으로 Error가 되어야하지만 허용됨.
		
		/* 하한 */
		ArrayList<? extends Worker> list2 = new ArrayList<>();
		list2.add(new Person()); // 하한이므로 Error!
		list2.add(new Worker()); // 자기 자신이므로 허용.
		list2.add(new EmartWorker()); // ??? 하한이므로 허용되어야 하지만 Error!
	}
	
	// Worker 자기 자신이거나 Worker의 부모들 허용. (상한)
	public static void test1(Info<? super Worker> obj) {
	}
	
	// Worker 자기 자신이거나 Worker의 자식들 허용. (하한)
	public static void test2(Info<? extends Worker> obj) {
	}
}



<? super T>이면 T이거나 T의 부모들을 허용,

<? extends T>이면 T이거나 T의 자식들을 허용 그렇게 알고 있었는 데,


Collections에서는 상당히 희한하게 동작하네요.


상한인 경우 반대로 동작하고, 하한인 경우 전체 다 Error이고...


문법적으로 상당히 이상한 데, 원리가 뭔지 궁금합니다.

0
2
  • 답변 2

  • fx
    1k
    2016-11-10 02:02:19

    해당 문법은 컬렉션에서 취급하는 객체에 대한 관계가 아닙니다.

    컬렉션 자체에 대한 관계입니다.




    컬렉션에서 타입 T에 대해서 T를 확장한 객체는 취급할 수 있습니다. 반대는 안되고요

    ArrayList<Worker> 라면 Worker 와 EmartWorker 는 되지만, Person 은 안되지요.


    super와 extends 는 컬렉션에서 취급하는 객체에 대한 제한이 아닙니다. 컬렉션 자체에 대한 제한입니다.

    EmartWorker는 Worker 대신 사용할 수 있지만,


    ArrayList<Worker> 와 ArrayList<EmartWorker> 는 서로 상속관계가 없으므로, 

    ArrayList<Woker> 인자를 받는 곳에 ArrayList<EmartWorker> 를 사용할 수 없습니다.

    Worker에 work() 라는 메소드가 있다고 하면, EmartWorker에도 work() 가 있을 것입니다.


    void workingWithList(ArrayList<Worker> workers) {

        for(Worker worker : workers) {

            worker.work();

        }

    }


    위 메소드에 ArrayList<EmartWorker> 형식의 emartWokers 를 보내면 형식이 달라 처리할 수 없습니다.

    하지만 EmartWorker가 Worker를 상속했기 때문에

    ArrayList<EmartWorker> 가 ArrayList<Worker> 를 상속한 관계 처럼 사용한다면 처리할 수 있을 것입니다.


    void workingWithList(ArrayList<? extends Worker> workers) {

        ..... 처럼 형식을 지정한다면, Worker를 상속한 어떤 타입에 대한 리스트를 인자로 받을 수 있게 됩니다.


    리스트에서 먼가를 꺼내쓰는 작업을 할때는 extends를 사용하면 되는데,

    반대로 리스트에 무엇인가를 추가할때는 super를 사용합니다.


    void addWorkerWithList(ArrayList<Worker> workers) {

        Worker worker = new Worker();

        ....

        workers.add(worker);

    }


    위 경우도 역시 ArrayList<Worker> 타입 외에는 사용할 수 없습니다. 그런데 이 경우에는

    ArrayList<EmartWorker>를 사용할 수는 없습니다.   EmartWorker 컬렉션에 Worker를 추가할 수는 없을 테니까요

    하지만 ArrayList<Person> 이라면 상관 없습니다. Worker는 Person 이기도 하니까요.


    void addWorkerWithList(ArrayList<? super Worker> workers) {

        Worker worker = new Worker();

        ....

        workers.add(worker);

    }


    메소드에는 ArrayList<Person> people 를 인자로 호출하게 되면 people에 worker 가 하나 추가될 것입니다.

    3
  • NPE
    533
    2016-11-10 02:59:42 작성 2016-11-10 03:00:00 수정됨

    아 관점이 잘못됐었네요.

    답변 감사합니다.

    0
  • 로그인을 하시면 답변을 등록할 수 있습니다.