Reports assignments of application services to static final fields / immutable properties.

Static final fields (Java) or static immutable properties with backing fields (Kotlin)

Note: Hereinafter, static in Kotlin refers to members of non-anonymous objects or top-level declarations.

Such services' assignments contribute to global state and make it impossible to tear down an application and set up another one in tests, therefore, repeated tests in the same process may fail. The only exception is an explicit constructor call to store dummy/default instances.

The recommended way to avoid storing services is to retrieve a service locally. Alternatively, one can wrap it in java.util.function.Supplier (Java, Kotlin) or convert the property to a function (Kotlin).

Example (Java):


// Bad:
private static final ManagingFS ourInstance = ApplicationManager.getApplication().getService(ManagingFS.class);

// Good:
private static final Supplier<ManagingFS> ourInstance = CachedSingletonsRegistry.lazy(() -> {
  return ApplicationManager.getApplication().getService(ManagingFS.class);
});

// Exception:
private static final UniqueVFilePathBuilder DUMMY_BUILDER = new UniqueVFilePathBuilder()

Retrieving a service instance through static immutable properties (Kotlin)

While services' assignments to properties without backing fields don't cause the aforementioned problem, using an explicit getInstance() method to retrieve a service is preferred over using a property:

For better tooling performance, it is always advised to keep an explicit method return type.

Example:


@Service
class MyApplicationService {
  companion object {
    @JvmStatic
    val instance: MyApplicationService // bad
       get() = service()
  }
}

@Service
class MyApplicationService {
  companion object {
    @JvmStatic
    fun getInstance(): MyApplicationService = service() // good
  }
}

New in 2023.3