siom79 commented on PR #1888:
URL: https://github.com/apache/maven-resolver/pull/1888#issuecomment-4557249511
### Root Cause
The errors stem from two independent changes that interact with each other:
**1. A deliberate API change in `GenericVersionScheme` (February 2023,
commit `7872699c`, PR #242)**
The three methods were changed from returning the abstract interface types
to returning their
concrete implementations (covariant return types):
| Method | Old return type | New return type |
|---|---|---|
| `parseVersion(String)` | `Version` | `GenericVersion` |
| `parseVersionRange(String)` | `VersionRange` | `GenericVersionRange` |
| `parseVersionConstraint(String)` | `VersionConstraint` |
`GenericVersionConstraint` |
The motivation was explicitly documented in the commit message: *"generic
scheme creates
generic components: usually is directly instantiated, this just made a lot
of casting issues."*
This is a legitimate and intentional improvement.
**2. A bug fix in japicmp 0.25.7 (issue #507, PR #508)**
Prior to 0.25.7, japicmp incorrectly matched non-bridge methods against
compiler-generated
bridge methods when comparing class versions. This caused the covariant
return type change
above to go undetected for years. The fix in 0.25.7 corrected the
bridge-method matching
logic, which now surfaces the 2023 change as `METHOD_RETURN_TYPE_CHANGED`.
### Is This a Real Binary Incompatibility?
**No.** Covariant return types are explicitly supported by the Java Language
Specification
(JLS ยง8.4.5) and are designed to be backward-compatible. When
`GenericVersionScheme`
declares `GenericVersion parseVersion(String)`, the Java compiler
automatically generates
a bridge method:
```
// real method (new callers use this)
public GenericVersion parseVersion(String) { ... }
// compiler-generated bridge (preserves binary compatibility for old callers)
public synthetic bridge Version parseVersion(String) {
return this.parseVersion(String); // delegates to real method
}
```
Any code compiled against the old API that calls `Version v =
scheme.parseVersion(...)` will
find the bridge method at runtime and continue to work without
recompilation. A
`NoSuchMethodError` cannot occur.
The only scenario where this change can cause a real (silent) breakage is
**subclassing**:
if a third party extends `GenericVersionScheme` and overrides `parseVersion`
with the old
`Version` return type, that override no longer overrides the non-bridge
method. This is a
legitimate concern, but it is a different and narrower category than a full
binary
incompatibility.
### Relationship to japicmp
The detection itself is correct โ the API *did* change in 2023 and japicmp
0.25.6 was
silently missing it due to its own bug. However, japicmp classifies
covariant return type
changes in the same category as genuinely incompatible return type changes,
which is
overly strict.
A dedicated issue has been filed with the japicmp project to introduce a
separate, less
severe classification for this pattern
(`METHOD_RETURN_TYPE_COVARIANT_CHANGED`), treated
as compatible by default:
๐ https://github.com/siom79/japicmp/issues/522
### Recommended Fix for This Project
Until japicmp handles covariant return type changes natively, the
appropriate fix on the
maven-resolver side is to add explicit exclusion rules in the japicmp
configuration for
these three methods. The 2023 change was intentional, well-motivated, and
binary-safe:
```xml
<excludes>
<exclude>
org.eclipse.aether.util.version.GenericVersionScheme#parseVersion(java.lang.String)
</exclude>
<exclude>
org.eclipse.aether.util.version.GenericVersionScheme#parseVersionRange(java.lang.String)
</exclude>
<exclude>
org.eclipse.aether.util.version.GenericVersionScheme#parseVersionConstraint(java.lang.String)
</exclude>
</excludes>
```
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]