88 lines
3.6 KiB
Plaintext
88 lines
3.6 KiB
Plaintext
== Why is this an issue?
|
|
|
|
A return type containing wildcards cannot be narrowed down in any context.
|
|
This indicates that the developer's intention was likely something else.
|
|
|
|
The core problem lies in type variance.
|
|
Expressions at an input position, such as arguments passed to a method,
|
|
can have a more specific type than the type expected by the method, which is called _covariance_.
|
|
Expressions at an output position, such as a variable that receives the return result from a method,
|
|
can have a more general type than the method's return type, which is called _contravariance_.
|
|
This can be traced back to the Liskov substitution principle.
|
|
|
|
In Java, type parameters of a generic type are invariant by default
|
|
due to their potential occurrence in both input and output positions at the same time.
|
|
A classic example of this is the methods `T get()` (output position) and `add(T element)` (input position)
|
|
in interface `java.util.List`.
|
|
We could construct cases with invalid typing in `List` if `T` were not invariant.
|
|
|
|
Wildcards can be employed to achieve covariance or contravariance in situations
|
|
where the type parameter appears in one position only:
|
|
|
|
- `<? extends Foo>` for covariance (input positions)
|
|
- `<? super Foo>` for contravariance (output positions)
|
|
|
|
However, covariance is ineffective for the return type of a method since it is not an input position.
|
|
Making it contravariant also has no effect since it is the receiver of the return value
|
|
which must be contravariant (use-site variance in Java).
|
|
Consequently, a return type containing wildcards is generally a mistake.
|
|
|
|
== How to fix it
|
|
|
|
The solution to this problem depends on the original intention of the developer. Given the examples:
|
|
|
|
[source,java,diff-id=1,diff-type=noncompliant]
|
|
----
|
|
List<? extends Animal> getAnimals() { ... } // Noncompliant, wildcard with no use
|
|
List<? super Plant> getLifeforms() { ... } // Noncompliant, wildcard with no use
|
|
----
|
|
|
|
You can remove the wildcards to make the types invariant:
|
|
|
|
[source,java,diff-id=1,diff-type=compliant]
|
|
----
|
|
List<Animal> getAnimals() { ... } // Compliant, using invariant type instead
|
|
List<Plant> getLifeforms() { ... } // Compliant, using invariant type instead
|
|
----
|
|
|
|
Or replace them with a super- or subtypes (still invariant):
|
|
|
|
[source,java]
|
|
----
|
|
List<Dog> getAnimals() { ... } // Compliant, using subtype instead
|
|
List<Lifeform> getLifeforms() { ... } // Compliant, using supertype instead
|
|
----
|
|
|
|
== Resources
|
|
|
|
=== Documentation
|
|
|
|
* https://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html[The Java™ Tutorials - Wildcards]
|
|
|
|
=== Articles & blog posts
|
|
|
|
* https://medium.com/javarevisited/variance-in-java-and-scala-63af925d21dc[Sinisa Louc - A Complete Guide to Variance in Java and Scala]
|
|
* https://kotlinexpertise.com/kotlin-generics-and-variance-vs-java[Kotlin Expertise Blog - Kotlin Generics and Variance (Compared to Java)]
|
|
* https://en.wikipedia.org/wiki/Covariance_and_contravariance_(computer_science)[Wikipedia - Covariance and contravariance (computer science)]
|
|
* https://schneide.blog/2015/05/11/declaration-site-and-use-site-variance-explained/[Schneide Blog - Declaration-site and use-site variance explained]
|
|
* https://en.wikipedia.org/wiki/Liskov_substitution_principle[Wikipedia - Liskov substitution principle]
|
|
|
|
ifdef::env-github,rspecator-view[]
|
|
|
|
'''
|
|
== Implementation Specification
|
|
(visible only on this page)
|
|
|
|
=== Message
|
|
|
|
Remove usage of generic wildcard type
|
|
|
|
'''
|
|
== Comments And Links
|
|
(visible only on this page)
|
|
|
|
=== on 1 Nov 2013, 19:22:11 Freddy Mallet wrote:
|
|
Is implemented by \https://jira.sonarsource.com/browse/SONARJAVA-374
|
|
|
|
endif::env-github,rspecator-view[]
|