<의 차이점은 무엇입니까? 슈퍼 E>와 <? E>를 확장합니까? 차이점은 무엇이며 <? super

차이점은 무엇이며 <? super E>그리고 <? extends E>?

예를 들어 클래스를 살펴보면 java.util.concurrent.LinkedBlockingQueue생성자에 대해 다음과 같은 서명이 있습니다.

public LinkedBlockingQueue(Collection<? extends E> c)

그리고 방법에 대한 하나 :

public int drainTo(Collection<? super E> c)


답변

첫 번째는 그것이 “E의 조상 인 어떤 유형”이라고 말합니다. 두 번째는 “E의 서브 클래스 인 일부 유형”이라고 말합니다. (두 경우 모두 E 자체는 괜찮습니다.)

생성자가 사용하는 그래서 ? extends E그것이 때 보장 있도록 양식을 가져옵니다 컬렉션에서 값을, 그들은 모두 E 또는 일부 서브 클래스 (즉 그것은 호환). drainTo있어서 값을 넣어 시도 컬렉션의 요소 타입 가져야하므로, 수집 E 또는 수퍼 클래스 .

예를 들어, 다음과 같은 클래스 계층 구조가 있다고 가정하십시오.

Parent extends Object
Child extends Parent

그리고 LinkedBlockingQueue<Parent>. List<Child>모든 Child것이 부모 이기 때문에 모든 요소를 ​​안전하게 복사 하는 전달을 구성 할 수 있습니다 . 당신은 전달할 수없는 List<Object>몇 가지 요소와 호환되지 않을 수 있기 때문입니다Parent .

마찬가지로 List<Object>모든 Parent것이 Object… 이기 때문에 대기열을 비울 수는 있지만 모든 요소가와 호환되기를 기대 List<Child>하기 때문에 비울 수 없었 List<Child>습니다 Child.


답변

그 이유는 Java가 제네릭을 구현하는 방법을 기반으로합니다.

배열 예제

배열을 사용하면이 작업을 수행 할 수 있습니다 (배열은 공변량입니다)

Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;

그러나 이렇게하면 어떻게 될까요?

myNumber[0] = 3.14; //attempt of heap pollution

이 마지막 줄은 잘 컴파일되지만이 코드를 실행하면 ArrayStoreException . 숫자 참조를 통해 액세스하는 것과 상관없이 정수 배열에 double을 넣으려고하기 때문에.

즉, 컴파일러를 속일 수는 있지만 런타임 유형 시스템을 속일 수는 없습니다. 배열은 우리가 reifiable 타입 이라고 부르기 때문 입니다. 즉, 런타임시 Java는이 배열이 실제로 유형의 참조를 통해 액세스되는 정수 배열로 인스턴스화되었음을 알고 있습니다.Number[] .

보시다시피, 하나는 객체의 실제 유형이고 다른 하나는 객체에 액세스하는 데 사용하는 참조 유형입니다.

자바 제네릭의 문제점

이제 Java 제네릭 형식의 문제점은 형식 정보가 컴파일러에 의해 삭제되어 런타임에 사용할 수 없다는 것입니다. 이 과정을 유형 삭제 라고 합니다 . Java에서 이와 같은 제네릭을 구현해야 할 충분한 이유가 있지만, 그것은 긴 이야기이며, 무엇보다도 기존 코드와 바이너리 호환성을 가지고 수행해야합니다 (제네릭을 얻는 방법 참조) ).

그러나 여기서 중요한 점은 런타임에 유형 정보가 없기 때문에 힙 오염을 저 지르지 않도록 보장 할 방법이 없다는 것입니다.

예를 들어

List<Integer> myInts = new ArrayList<Integer>();
myInts.add(1);
myInts.add(2);

List<Number> myNums = myInts; //compiler error
myNums.add(3.14); //heap pollution

Java 컴파일러가이를 수행하지 못하게하는 경우 런타임시 시스템이이 목록이 정수 목록으로 만 가정되었음을 판별 할 방법이 없기 때문에 런타임을 중지 할 수 없습니다. Java 런타임을 사용하면 정수만 포함해야 할 때이 목록에 원하는 것을 넣을 수 있습니다. 생성 될 때 정수 목록으로 선언 되었기 때문입니다.

따라서 Java 디자이너는 컴파일러를 속일 수 없도록했습니다. 컴파일러를 속일 수 없다면 (배열로 할 수있는 것처럼) 런타임 유형 시스템을 속일 수 없습니다.

따라서 제네릭 형식은 수정할 수 없다고 말합니다. .

분명히 이것은 다형성을 방해 할 것입니다. 다음 예제를 고려하십시오.

static long sum(Number[] numbers) {
   long summation = 0;
   for(Number number : numbers) {
      summation += number.longValue();
   }
   return summation;
}

이제 다음과 같이 사용할 수 있습니다.

Integer[] myInts = {1,2,3,4,5};
Long[] myLongs = {1L, 2L, 3L, 4L, 5L};
Double[] myDoubles = {1.0, 2.0, 3.0, 4.0, 5.0};

System.out.println(sum(myInts));
System.out.println(sum(myLongs));
System.out.println(sum(myDoubles));

그러나 일반 컬렉션으로 동일한 코드를 구현하려고하면 성공하지 못합니다.

static long sum(List<Number> numbers) {
   long summation = 0;
   for(Number number : numbers) {
      summation += number.longValue();
   }
   return summation;
}

시도하면 컴파일러 오류가 발생합니다 …

List<Integer> myInts = asList(1,2,3,4,5);
List<Long> myLongs = asList(1L, 2L, 3L, 4L, 5L);
List<Double> myDoubles = asList(1.0, 2.0, 3.0, 4.0, 5.0);

System.out.println(sum(myInts)); //compiler error
System.out.println(sum(myLongs)); //compiler error
System.out.println(sum(myDoubles)); //compiler error

해결책은 공분산 및 공분산으로 알려진 Java 제네릭의 두 가지 강력한 기능을 사용하는 방법을 배우는 것입니다.

공분산

공분산을 사용하면 구조에서 항목을 읽을 수 있지만 아무 것도 쓸 수 없습니다. 이들은 모두 유효한 선언입니다.

List<? extends Number> myNums = new ArrayList<Integer>();
List<? extends Number> myNums = new ArrayList<Float>();
List<? extends Number> myNums = new ArrayList<Double>();

그리고 당신은 읽을 수 있습니다 myNums:

Number n = myNums.get(0); 

실제 목록에 포함 된 것을 확실하게 알 수 있기 때문에 Number로 업 캐스트 될 수 있습니다 (Number를 확장하는 모든 것이 숫자입니다)?

그러나 공변량 구조에 어떤 것도 넣을 수 없습니다.

myNumst.add(45L); //compiler error

Java는 일반 구조에서 객체의 실제 유형을 보증 할 수 없으므로 허용되지 않습니다. Number를 확장하는 것은 무엇이든 될 수 있지만 컴파일러는 확신 할 수 없습니다. 따라서 읽을 수는 있지만 쓸 수는 없습니다.

공분산

불균형을 사용하면 반대의 작업을 수행 할 수 있습니다. 일반적인 구조에 물건을 넣을 수는 있지만 읽을 수는 없습니다.

List<Object> myObjs = new List<Object>();
myObjs.add("Luke");
myObjs.add("Obi-wan");

List<? super Number> myNums = myObjs;
myNums.add(10);
myNums.add(3.14);

이 경우 객체의 실제 특성은 객체 목록이며, 불균형을 통해 기본적으로 모든 숫자에 공통 조상으로 Object가 있으므로 Number를 넣을 수 있습니다. 따라서 모든 숫자는 개체이므로 유효합니다.

그러나 숫자를 얻는다고 가정하면이 반 변형 구조에서 아무것도 읽을 수 없습니다.

Number myNum = myNums.get(0); //compiler-error

보시다시피, 컴파일러에서이 줄을 쓸 수 있으면 런타임에 ClassCastException이 발생합니다.

원리를 얻는다

따라서 구조에서 일반 값만 가져 오려는 경우 공분산을 사용하고 구조에 일반 값을 넣을 때만 대비를 사용하고 두 가지를 모두 수행하려는 경우 정확한 일반 유형을 사용하십시오.

내가 가진 가장 좋은 예는 한 목록에서 다른 목록으로 모든 종류의 숫자를 복사하는 다음입니다. 소스 에서만 항목을 가져오고 대상 에만 항목을 넣습니다 .

public static void copy(List<? extends Number> source, List<? super Number> target) {
    for(Number number : source) {
        target(number);
    }
}

공분산과 공분산의 힘으로 인해 다음과 같은 경우에 작동합니다.

List<Integer> myInts = asList(1,2,3,4);
List<Double> myDoubles = asList(3.14, 6.28);
List<Object> myObjs = new ArrayList<Object>();

copy(myInts, myObjs);
copy(myDoubles, myObjs);

답변

<? extends E>E상한으로 정의합니다 . “E .

<? super E>E하한값으로 정의합니다 : ” E이것에 캐스트 할 수 있습니다.”


답변

나는 이것을 시도하고 대답 할 것입니다. 그러나 정답을 얻으려면 Joshua Bloch의 책 Effective Java (2 판)를 확인해야합니다. 그는 “프로듀서 확장, 소비자 슈퍼”를 의미하는 니모닉 PECS에 대해 설명합니다.

아이디어는 코드에서 객체의 일반 값을 소비하는 경우 extends를 사용해야한다는 것입니다. 그러나 제네릭 형식의 새 값을 생성하는 경우 super를 사용해야합니다.

예를 들어 :

public void pushAll(Iterable<? extends E> src) {
  for (E e: src)
    push(e);
}

public void popAll(Collection<? super E> dst) {
  while (!isEmpty())
    dst.add(pop())
}

그러나 실제로이 책을 확인해야합니다 :
http://java.sun.com/docs/books/effective/


답변

<? super E> 방법 any object including E that is parent of E

<? extends E> 방법 any object including E that is child of E .


답변

반도체 ( ) 및 공분산 ( ) 이라는 용어를 Google에 표시 할 수 있습니다 . 제네릭을 이해할 때 가장 유용한 것은 다음의 메소드 서명을 이해하는 것입니다 .<? super E><? extends E>Collection.addAll

public interface Collection<T> {
    public boolean addAll(Collection<? extends T> c);
}

그냥 당신은 추가 할 수 있도록하려는 것 같은 StringA를 List<Object>:

List<Object> lo = ...
lo.add("Hello")

또한 메소드 를 통해 List<String>(또는 Strings 모음) 을 추가 할 수 있어야합니다 addAll.

List<String> ls = ...
lo.addAll(ls)

그러나 a List<Object>와 a List<String>는 동등하지 않으며 후자는 전자의 하위 클래스도 아닙니다. 공변량 유형 매개 변수 의 개념이 필요합니다. 즉<? extends T> 비트 .

이것을 가지고 나면, 반공산 도 를 원하는 시나리오를 생각하는 것은 간단합니다 ( Comparable인터페이스를 확인하십시오 ).


답변

대답하기 전에; 명확하게

  1. 제네릭은 TYPE_SAFETY를 보장하기 위해 컴파일 시간 기능 만 제공하며 RUNTIME 동안에는 사용할 수 없습니다.
  2. 제네릭을 사용한 참조 만 형식 안전성을 강제합니다. 참조가 제네릭으로 선언되지 않으면 safty 유형없이 작동합니다.

예:

List stringList = new ArrayList<String>();
stringList.add(new Integer(10)); // will be successful.

이것이 와일드 카드를보다 명확하게 이해하는 데 도움이되기를 바랍니다.

//NOTE CE - Compilation Error
//      4 - For

class A {}

class B extends A {}

public class Test {

    public static void main(String args[]) {

        A aObj = new A();
        B bObj = new B();

        //We can add object of same type (A) or its subType is legal
        List<A> list_A = new ArrayList<A>();
        list_A.add(aObj);
        list_A.add(bObj); // A aObj = new B(); //Valid
        //list_A.add(new String()); Compilation error (CE);
        //can't add other type   A aObj != new String();


        //We can add object of same type (B) or its subType is legal
        List<B> list_B = new ArrayList<B>();
        //list_B.add(aObj); CE; can't add super type obj to subclass reference
        //Above is wrong similar like B bObj = new A(); which is wrong
        list_B.add(bObj);



        //Wild card (?) must only come for the reference (left side)
        //Both the below are wrong;   
        //List<? super A> wildCard_Wrongly_Used = new ArrayList<? super A>();
        //List<? extends A> wildCard_Wrongly_Used = new ArrayList<? extends A>();


        //Both <? extends A>; and <? super A> reference will accept = new ArrayList<A>
        List<? super A> list_4__A_AND_SuperClass_A = new ArrayList<A>();
                        list_4__A_AND_SuperClass_A = new ArrayList<Object>();
                      //list_4_A_AND_SuperClass_A = new ArrayList<B>(); CE B is SubClass of A
                      //list_4_A_AND_SuperClass_A = new ArrayList<String>(); CE String is not super of A  
        List<? extends A> list_4__A_AND_SubClass_A = new ArrayList<A>();
                          list_4__A_AND_SubClass_A = new ArrayList<B>();
                        //list_4__A_AND_SubClass_A = new ArrayList<Object>(); CE Object is SuperClass of A


        //CE; super reference, only accepts list of A or its super classes.
        //List<? super A> list_4__A_AND_SuperClass_A = new ArrayList<String>(); 

        //CE; extends reference, only accepts list of A or its sub classes.
        //List<? extends A> list_4__A_AND_SubClass_A = new ArrayList<Object>();

        //With super keyword we can use the same reference to add objects
        //Any sub class object can be assigned to super class reference (A)                  
        list_4__A_AND_SuperClass_A.add(aObj);
        list_4__A_AND_SuperClass_A.add(bObj); // A aObj = new B();
        //list_4__A_AND_SuperClass_A.add(new Object()); // A aObj != new Object(); 
        //list_4__A_AND_SuperClass_A.add(new String()); CE can't add other type

        //We can't put anything into "? extends" structure. 
        //list_4__A_AND_SubClass_A.add(aObj); compilation error
        //list_4__A_AND_SubClass_A.add(bObj); compilation error
        //list_4__A_AND_SubClass_A.add("");   compilation error

        //The Reason is below        
        //List<Apple> apples = new ArrayList<Apple>();
        //List<? extends Fruit> fruits = apples;
        //fruits.add(new Strawberry()); THIS IS WORNG :)

        //Use the ? extends wildcard if you need to retrieve object from a data structure.
        //Use the ? super wildcard if you need to put objects in a data structure.
        //If you need to do both things, don't use any wildcard.


        //Another Solution
        //We need a strong reference(without wild card) to add objects 
        list_A = (ArrayList<A>) list_4__A_AND_SubClass_A;
        list_A.add(aObj);
        list_A.add(bObj);

        list_B = (List<B>) list_4__A_AND_SubClass_A;
        //list_B.add(aObj); compilation error
        list_B.add(bObj);

        private Map<Class<? extends Animal>, List<? extends Animal>> animalListMap;

        public void registerAnimal(Class<? extends Animal> animalClass, Animal animalObject) {

            if (animalListMap.containsKey(animalClass)) {
                //Append to the existing List
                 /*    The ? extends Animal is a wildcard bounded by the Animal class. So animalListMap.get(animalObject);
                 could return a List<Donkey>, List<Mouse>, List<Pikachu>, assuming Donkey, Mouse, and Pikachu were all sub classes of Animal.
                 However, with the wildcard, you are telling the compiler that you don't care what the actual type is as long as it is a sub type of Animal.
                 */
                //List<? extends Animal> animalList = animalListMap.get(animalObject);
                //animalList.add(animalObject);  //Compilation Error because of List<? extends Animal>
                List<Animal> animalList = animalListMap.get(animalObject);
                animalList.add(animalObject);


            }
    }

    }
}