rspec/rules/S1452/java/rule.adoc
2023-08-11 16:49:04 +02:00

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[]