programing

자바 : 제한된 와일드 카드 또는 제한된 유형 매개 변수?

nasanasas 2020. 11. 11. 20:14
반응형

자바 : 제한된 와일드 카드 또는 제한된 유형 매개 변수?


최근에 다음 기사를 읽었습니다. http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html

내 질문은 다음과 같은 방법을 만드는 대신입니다.

public void drawAll(List<? extends Shape> shapes){
    for (Shape s: shapes) {
        s.draw(this);
    }
}

다음과 같은 메서드를 만들 수 있으며 제대로 작동합니다.

public <T extends Shape> void drawAll(List<T> shapes){
    for (Shape s: shapes) {
        s.draw(this);
    }
}

어떤 방법을 사용해야합니까? 이 경우 와일드 카드가 유용합니까?


그것은 당신이 무엇에 따라 필요 할 수 있습니다. 다음과 같은 작업을 수행하려면 제한된 유형 매개 변수를 사용해야합니다.

public <T extends Shape> void addIfPretty(List<T> shapes, T shape) {
    if (shape.isPretty()) {
       shapes.add(shape);
    }
}

여기에 a List<T> shapes와 a T shape가 있으므로 안전하게 할 수 shapes.add(shape)있습니다. 선언 된 경우 안전하게 List<? extends Shape>이동할 수 없습니다add (a List<Square>및 a 가있을 수 있기 때문에 Circle).

따라서 제한된 유형 매개 변수에 이름을 지정하여 일반 메소드의 다른 곳에서 사용할 수 있습니다. 물론이 정보가 항상 필요한 것은 아니므로 유형 (예 :)에 대해 많이 알 필요가없는 경우 drawAll와일드 카드만으로 충분합니다.

경계 유형 매개 변수를 다시 참조하지 않더라도 경계가 여러 개인 경우 경계 유형 매개 변수가 여전히 필요합니다. 다음은 Angelika Langer의 Java Generics FAQ 에서 인용 한 것입니다.

와일드 카드 바운드와 유형 매개 변수 바운드의 차이점은 무엇입니까?

와일드 카드는 하나의 경계 만 가질 수 있지만 유형 매개 변수는 여러 경계를 가질 수 있습니다. 와일드 카드는 하한 또는 상한을 가질 수 있지만 유형 매개 변수에 대한 하한과 같은 것은 없습니다.

와일드 카드 경계와 유형 매개 변수 경계는 둘 다 경계라고 불리며 부분적으로 유사한 구문을 갖기 때문에 종종 혼동됩니다. […]

구문 :

  type parameter bound     T extends Class & Interface1 & … & InterfaceN

  wildcard bound  
      upper bound          ? extends SuperType
      lower bound          ? super   SubType

와일드 카드는 하한 또는 상한 중 하나만 가질 수 있습니다. 와일드 카드 경계 목록은 허용되지 않습니다.

constrast에서 유형 매개 변수는 여러 경계를 가질 수 있지만 유형 매개 변수에 대한 하한은 없습니다.

Effective Java 2nd Edition, Item 28의 인용문 : 제한된 와일드 카드를 사용하여 API 유연성을 높입니다 .

유연성을 극대화하려면 생산자 또는 소비자를 나타내는 입력 매개 변수에 와일드 카드 유형을 사용하십시오. […] PECS는 생산자 extends, 소비자를 의미합니다. super[…]

반환 유형으로 와일드 카드 유형을 사용하지 마십시오 . 사용자에게 추가적인 유연성을 제공하는 대신 클라이언트 코드에서 와일드 카드 유형을 사용하도록 강제합니다. 올바르게 사용 된 와일드 카드 유형은 클래스 사용자에게 거의 보이지 않습니다. 이는 메소드가 허용해야하는 매개 변수를 허용하고 거부해야하는 매개 변수를 거부하도록합니다. 클래스 사용자가 와일드 카드 유형에 대해 생각해야한다면 클래스의 API에 문제가있을 수 있습니다.

PECS 원칙을 적용하여 이제 addIfPretty예제 로 돌아가서 다음을 작성하여 더 유연하게 만들 수 있습니다.

public <T extends Shape> void addIfPretty(List<? super T> list, T shape) { … }

이제 우리는 addIfPrettya Circle, a List<Object>. 이것은 명백히 typesafe하지만 우리의 원래 선언은 그것을 허용 할만큼 충분히 유연하지 않았습니다.

관련 질문


요약

  • 제한된 유형 매개 변수 / 와일드 카드를 사용하면 API의 유연성이 향상됩니다.
  • 유형에 여러 매개 변수가 필요한 경우 경계 유형 매개 변수를 사용할 수밖에 없습니다.
  • 유형에 하한이 필요한 경우 경계 와일드 카드를 사용할 수밖에 없습니다.
  • "생산자"는 상한을, "소비자"는 하한을 가짐
  • 반환 유형에 와일드 카드를 사용하지 마십시오.

귀하의 예에서는 T를 사용할 필요가 없습니다. 다른 곳에서는 해당 유형을 사용하지 않기 때문입니다.

하지만 다음과 같이했다면 :

public <T extends Shape> T drawFirstAndReturnIt(List<T> shapes){
    T s = shapes.get(0);
    s.draw(this);
    return s;
}

또는 polygenlubricants가 말한 것처럼 목록의 유형 매개 변수를 다른 유형 매개 변수와 일치 시키려면 다음을 수행하십시오.

public <T extends Shape> void mergeThenDraw(List<T> shapes1, List<T> shapes2) {
    List<T> mergedList = new ArrayList<T>();
    mergedList.addAll(shapes1);
    mergedList.addAll(shapes2);
    for (Shape s: mergedList) {
        s.draw(this);
    }
}

In the first example you get a bit more type safety then returning just Shape, since you can then pass the result to a function that may take a child of Shape. For example you may pass a List<Square> to my method, and then pass the resulting Square to a method that only takes Squares. If you used '?' you would have to cast the resulting Shape to Square which would not be type safe.

In the second example you ensure that both lists have the same type parameter (which you can't do with '?', since each '?' is different), so that you can create a list that contains all elements from both of them.


Consider following example from The Java Programming by James Gosling 4th edition below where we want to merge 2 SinglyLinkQueue:

public static <T1, T2 extends T1> void merge(SinglyLinkQueue<T1> d, SinglyLinkQueue<T2> s){
    // merge s element into d
}

public static <T> void merge(SinglyLinkQueue<T> d, SinglyLinkQueue<? extends T> s){
        // merge s element into d
}

Both of the above methods have the same functionality. So which is preferable? Answer is 2nd one. In the author's own words :

"The general rule is to use wildcards when you can because code with wildcards is generally more readable than code with multiple type parameters. When deciding if you need a type variable, ask yourself if that type variable is used to relate two or more parameters, or to relate a parameter type with the return type. If the answer is no, then a wildcard should suffice."

Note: In book only second method is given and type parameter name is S instead of 'T'. First method is not there in the book.


As far as I understand, the wildcard allows for more concise code in situations where a type parameter is not required (e.g. because it's referenced at several places or because multiple bounds are required as detailed in other answers).

In the link you indicate I read (under "Generic Methods") the following statements which hint in this direction:

Generic methods allow type parameters to be used to express dependencies among the types of one or more arguments to a method and/or its return type. If there isn't such a dependency, a generic method should not be used.

[...]

Using wildcards is clearer and more concise than declaring explicit type parameters, and should therefore be preferred whenever possible.

[...]

Wildcards also have the advantage that they can be used outside of method signatures, as the types of fields, local variables and arrays.


The second way is a bit more verbose, but it allows you to refer T inside it:

for (T shape : shapes) {
    ...
}

That's the only difference, as far as I understand.

참고URL : https://stackoverflow.com/questions/3486689/java-bounded-wildcards-or-bounded-type-parameter

반응형