Skip to content

Conversation

lmcrean
Copy link

@lmcrean lmcrean commented Sep 7, 2025

Problem

Guava's TypeResolver creates TypeVariable implementations that lack annotation support, breaking Java 8+ AnnotatedElement compatibility. This affects modern reflection frameworks like Spring, Jackson, and validation libraries.

Current Issue:

  • Resolved TypeVariables don't implement AnnotatedElement methods
  • Annotation information is lost during type resolution
  • Frameworks expecting annotation access fail or behave incorrectly

Maintainer Context:
Addresses explicit TODO in TypeResolver.java:371-374 (commit 6dede7c):

/*
 * TODO: b/147144588 - But what about when the TypeVariable has annotations? Our
 * implementation currently doesn't support annotations _at all_.
 */

Solution

  1. Enhanced TypeVariableImpl: Made TypeVariableImpl implement AnnotatedElement interface
  2. Annotation Preservation: Added methods to extract and store annotations from original TypeVariable
  3. Cross-Platform Support: Implemented Android/JDK compatibility using reflection fallbacks
  4. API Extension: Added overloaded newArtificialTypeVariable method accepting original TypeVariable

Key Changes:

  • Types.java: Enhanced TypeVariableImpl with AnnotatedElement implementation
  • TypeResolver.java: Updated to pass original TypeVariable for annotation preservation
  • Added annotation extraction methods with platform-specific handling

Impact

Positive:

  • Enables annotation-aware reflection on resolved TypeVariables
  • Maintains full backward compatibility (purely additive)
  • Follows existing codebase patterns (Parameter.java implementation)
  • Resolves maintainer-identified technical debt

Framework Compatibility:

  • Spring dependency injection with generic type annotations
  • Jackson serialization with annotated type parameters
  • Validation frameworks using constraint annotations on generics

Testing guide

  • tests confirmed to pass as expected on local machine
# Core functionality
./mvnw test -f guava-tests/pom.xml -Dtest="TypeResolverTest,TypesTest"

# Android compatibility (run after building dependencies)
./mvnw install -f android/guava/pom.xml -DskipTests -q
./mvnw install -f android/guava-testlib/pom.xml -DskipTests -q  
./mvnw test -f android/guava-tests/pom.xml -Dtest="TypeResolverTest,TypesTest"

Breaking Changes

None. This is a purely additive enhancement:

  • Existing newArtificialTypeVariable method unchanged
  • New overloaded method added for annotation support
  • TypeVariableImpl maintains all existing behavior
  • No changes to public APIs or method signatures

Compatibility:

  • Maintains Android compatibility through reflection fallbacks
  • Graceful fallback to empty annotations when extraction fails
  • No performance impact when annotations not used

Enhance `Types` class with methods to create artificial type variables that preserve annotations and annotated bounds. Update `TypeVariableImpl` to implement `AnnotatedElement` interface, allowing retrieval of annotations. This change improves compatibility and functionality for type variable management.

RELNOTES=n/a
@lmcrean
Copy link
Author

lmcrean commented Sep 7, 2025

@cpovirk I took a shot at implementing the TypeVariable annotation support you identified in TODO: b/147144588. Does this approach align with what you had in mind? Happy to adjust if needed.

@cpovirk
Copy link
Member

cpovirk commented Sep 10, 2025

Unfortunately, a big part of the problem is that I don't even know what behavior would make sense here :( I am a little fuzzy at the moment even on the exact circumstances that we end up using our own TypeVariable implementation in the first place (capture conversion? substitution (e.g., resolveType, where)?). Then there's the question of what should happen to annotations during such operations, especially given that the handling in javac itself has changed over time. It's also possible that "proper" handling of annotations would mean returning our own TypeVariable implementation more often instead of the original JDK object (e.g., when substituting in @Nullable E for E?), and that could break existing users.

We say every so often that we should get together a list of issues for which it's reasonably like that we actually would try to take a patch. The most recent such list that I have covers:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants