program story

Java Generics (와일드 카드)

inputbox 2020. 8. 9. 10:25
반응형

Java Generics (와일드 카드)


Java의 일반 와일드 카드에 대한 몇 가지 질문이 있습니다.

  1. List<? extends T>의 차이점은 무엇입니까 List<? super T>?

  2. 제한 와일드 카드 란 무엇이며 제한되지 않은 와일드 카드 란 무엇입니까?


첫 번째 질문에서 <? extends T><? super T>바운드 와일드 카드의 예입니다. 제한되지 않은 와일드 카드는 다음과 같 <?>으며 기본적으로 의미 <? extends Object>합니다. 느슨하게 제네릭이 모든 유형이 될 수 있음을 의미합니다. 경계 와일드 카드 ( <? extends T>또는 <? super T>) 그 어느한다는 것을 말하며 종류에 제한을 둔다 연장 특정 유형 ( <? extends T>상한로 알려져있다), 또는 특정 유형 (의 조상이어야 <? super T>하한이라고 함) .

Java Tutorials에는 WildcardsMore Fun with Wildcards 기사에서 제네릭에 대한 꽤 좋은 설명이 있습니다.


클래스 계층 구조 A가있는 경우 B는 A의 하위 클래스이고 C와 D는 모두 아래와 같이 B의 하위 클래스입니다.

class A {}
class B extends A {}
class C extends B {}
class D extends B {}

그때

List<? extends A> la;
la = new ArrayList<B>();
la = new ArrayList<C>();
la = new ArrayList<D>();

List<? super B> lb;
lb = new ArrayList<A>(); //fine
lb = new ArrayList<C>(); //will not compile

public void someMethod(List<? extends B> lb) {
    B b = lb.get(0); // is fine
    lb.add(new C()); //will not compile as we do not know the type of the list, only that it is bounded above by B
}

public void otherMethod(List<? super B> lb) {
    B b = lb.get(0); // will not compile as we do not know whether the list is of type B, it may be a List<A> and only contain instances of A
    lb.add(new B()); // is fine, as we know that it will be a super type of A 
}

제한된 와일드 카드는 ? extends BB가 어떤 유형 인 경우 와 같습니다 . 즉, 유형을 알 수 없지만 "바운드"를 배치 할 수 있습니다. 이 경우 B의 하위 클래스 인 일부 클래스에 의해 제한됩니다.


조쉬 블로흐도 사용하는 경우의 좋은 설명이 있습니다 superextends이의 비디오 이야기 IO 구글 그가 언급 생산자 extends소비자super 니모닉.

프레젠테이션 슬라이드에서 :

대량 메서드를 추가하려는 경우 Stack<E>

void pushAll(Collection<? extends E> src);

– src는 E 생산자입니다.

void popAll(Collection<? super E> dst);

– dst는 E 소비자입니다.


유형 매개 변수에 전달 될 수있는 유형의 종류를 제한하려는 경우가있을 수 있습니다. 예를 들어, 숫자에 대해 작동하는 메서드는 Number 또는 해당 하위 클래스의 인스턴스 만 허용하려고 할 수 있습니다. 이것이 경계 유형 매개 변수의 용도입니다.

Collection<? extends MyObject> 

즉, MyObject와 IS-A 관계를 가진 모든 개체 (즉, myObject의 유형 인 모든 개체 또는 MyObject의 모든 하위 클래스의 개체라고 말할 수 있음) 또는 MyObject 클래스의 개체를 허용 할 수 있습니다.

예를 들면 :

class MyObject {}

class YourObject extends MyObject{}

class OurObject extends MyObject{}

그때,

Collection<? extends MyObject> myObject; 

MyObject 또는 MyObject의 자식 (즉, OurObject, YourObject 또는 MyObject 유형의 모든 개체, MyObject의 수퍼 클래스 개체 제외) 만 허용합니다.


일반적으로

If a structure contains elements with a type of the form ? extends E, we can get elements out of the structure, but we cannot put elements into the structure

List<Integer> ints = new ArrayList<Integer>();
ints.add(1);
ints.add(2);
List<? extends Number> nums = ints;
nums.add(3.14); // compile-time error
assert ints.toString().equals("[1, 2, 3.14]"); 

To put elements into the structure we need another kind of wildcard called Wildcards with super,

 List<Object> objs = Arrays.<Object>asList(2, 3.14, "four");
    List<Integer> ints = Arrays.asList(5, 6);
    Collections.copy(objs, ints);
    assert objs.toString().equals("[5, 6, four]");

    public static <T> void copy(List<? super T> dst, List<? extends T> src) {
          for (int i = 0; i < src.size(); i++) {
                dst.set(i, src.get(i));
         }
    }

Pre Requirements

public class A { }
public class B extends A { }
public class C extends A { }

List<A> listA = new ArrayList<A>();
List<B> listB = new ArrayList<B>();

The problem

listB = listA; //not compiled
listA = listB; //not compiled

listB = listA; In listA you can insert objects that are either instances of A, or subclasses of A (B and C). When you make listB = listA you could risk that listA contains non-B objects. When you then try to get objects out of listB you could risk to get non-B objects out (e.g. an A or a C). That breaks the contract of the listB variable declaration.

listA = listB; If you could make this assignment, it would be possible to insert A and C instances into the List pointed to by listB. You could do that via the listA reference, which is declared to be of List. Thus you could insert non-B objects into a list declared to hold B (or B subclass) instances.

enter image description here

The problem

As you see the problem is in assignments. For example you can not create a method that processes List's elements that are relatives, in this manner.

The solution

Generic Wildcards to the rescue

You should use List <? extends A>aka upper bound aka covariance aka producers if you are going to read .get() from the list

When you know that the instances in the collection are of instances of A or subclasses of A, it is safe to read the instances of the collection and cast them to A instances.

You can not insert elements into the list, because you don't know if the list is typed to the class A, B or C.

You should use List <? super A>aka lower bound aka contravariance aka consumers if you are going to insert .add() into the list

When you know that the list is typed to either A, or a superclass of A, it is safe to insert instances of A or subclasses of A (e.g. B or C) into the list.

You cannot read from the list though, except if it casts the read objects to Object. The elements already present in the list could be of any type that is either an A or superclass of A, but it is not possible to know exactly which class it is.

enter image description here

Please read more here


Generic wildcards are created to make methods that operate on Collection more reusable.

For example, if a method has a parameter List<A>, we can only give List<A> to this method. It is a waste for this method's funtion under some circumstances:

  1. If this method only reads objects from List<A>, then we should be allowed to give List<A-sub> to this method. (Because A-sub IS a A)
  2. If this method only inserts objects to List<A>, then we should be allowed to give List<A-super> to this method. (Because A IS a A-super)

참고URL : https://stackoverflow.com/questions/252055/java-generics-wildcards

반응형