Skip to content

Nullability definition in RetryTemplate - potential issue? #36312

@the-vj

Description

@the-vj

The current RetryTemplate (7.0.3) has method:

@Override public <R extends @Nullable Object> R execute(Retryable<R> retryable) throws RetryException

What we want to indicate there is that the return value can be null, but e.g. IntelliJ does not understand it that way - see sample below (not executable, but compiles and shows the problem).

IntelliJ will claim that the @Nullable annotation on executeRequest() return type is unnecessary because it never returns null - but it clearly does, since doExecuteRequest() can return null and e.g. omitting @Nullable in doExecuteRequest() return type creates compiler warnings - as one would expect, based on definition of nullability for ResponseExtractor.

public class AA {

	// just to have a variable reference for the example, not meant to execute
	ClientHttpResponse response = null;

	RetryTemplate retryTemplate = new RetryTemplate();

	public <T> @Nullable T executeRequest(ResponseExtractor<T> responseExtractor) throws RetryException {
		return retryTemplate.execute(() -> doExecuteRequest(responseExtractor));
	}

	private <T> @Nullable T doExecuteRequest(ResponseExtractor<T> responseExtractor) throws IOException {
		return responseExtractor.extractData(response);
	}

}

Could it be that the RetryTemplate method signature should actually look more like this?

@Override public <R> @Nullable R execute(Retryable<@Nullable R> retryable) throws RetryException

Or something like that?

At least after a bit of consulting the internet, Copilot and whatever else, I tend to think that the current observation is not a bug in IntelliJ, but actually a problem with the annotations in RetryTemplate.

I am not a JSpecify expert, while I did quite a bit of refactoring to take it into use, this is still relatively new to me - so I might be missing something... please comment.

BTW, I am using IntelliJ 2025.3.1.

When I add the following:

@FunctionalInterface
interface SameButDifferent {
	<R> @Nullable R execute(Retryable<@Nullable R> retryable) throws RetryException;
}


SameButDifferent sTemplate = null;


public <T> @Nullable T executeRequest2(ResponseExtractor<T> responseExtractor) throws RetryException {
	return sTemplate.execute(() -> doExecuteRequest(responseExtractor));
}

Then there is no nullability warning on executeRequest2().

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)status: waiting-for-feedbackWe need additional information before we can continuestatus: waiting-for-triageAn issue we've not yet triaged or decided on

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions