rspec/rules/S1452/java/rule.adoc

88 lines
3.6 KiB
Plaintext
Raw Normal View History

== 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.
2021-04-28 16:49:39 +02:00
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.
2021-04-28 16:49:39 +02:00
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.
2021-04-28 16:49:39 +02:00
Wildcards can be employed to achieve covariance or contravariance in situations
where the type parameter appears in one position only:
2021-04-28 16:49:39 +02:00
- `<? extends Foo>` for covariance (input positions)
- `<? super Foo>` for contravariance (output positions)
2021-04-28 16:49:39 +02:00
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
2021-04-28 16:49:39 +02:00
The solution to this problem depends on the original intention of the developer. Given the examples:
[source,java,diff-id=1,diff-type=noncompliant]
2021-04-28 16:49:39 +02:00
----
List<? extends Animal> getAnimals() { ... } // Noncompliant, wildcard with no use
List<? super Plant> getLifeforms() { ... } // Noncompliant, wildcard with no use
2021-04-28 16:49:39 +02:00
----
You can remove the wildcards to make the types invariant:
[source,java,diff-id=1,diff-type=compliant]
2021-04-28 16:49:39 +02:00
----
List<Animal> getAnimals() { ... } // Compliant, using invariant type instead
List<Plant> getLifeforms() { ... } // Compliant, using invariant type instead
2021-04-28 16:49:39 +02:00
----
Or replace them with a super- or subtypes (still invariant):
[source,java]
2021-04-28 16:49:39 +02:00
----
List<Dog> getAnimals() { ... } // Compliant, using subtype instead
List<Lifeform> getLifeforms() { ... } // Compliant, using supertype instead
2021-04-28 16:49:39 +02:00
----
== 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[]