Retrieving services
Any class given by a service provider can be injected into other service providers, requesting a service is as simple as declaring a parameter in the class's constructor, or the service factory's parameters.
Named services can be retrieved by using @ServiceName on the parameter,
this can be omitted if the parameter name matches a service with a compatible type.
Tip
You can also get services manually with BContext or ServiceContainer, the latter has all methods available, 
including Kotlin extensions.
Example
@BService // Enables the service to request services and be requested
public class TagDatabase { /* */ }
@Command // Enables the command to request services and be requested
public class TagCommand {
    private final Component components;
    private final TagDatabase tagDatabase;
    public TagCommand(
        // You can even request framework services, as long as they are annotated with @BService or @InterfacedService
        Component components,
        // and your own services
        TagDatabase tagDatabase
    ) {
        this.components = components;
        this.tagDatabase = tagDatabase;
    }
    /* */
}
@BService // Enables the service to request services and be requested
class TagDatabase { /* */ }
@Command // Enables the command to request services and be requested
class TagCommand(
    // You can even request framework services, as long as they are annotated with @BService or @InterfacedService
    // Here I've named it "componentsService" because "components" might conflict with some JDA-KTX builders
    private val componentsService: Components,
    // and your own services
    private val tagDatabase: TagDatabase
) {
    /* */
}
Retrieving services by name
Consider the following service providers:
@BService
class HttpClientProvider {
//    @Primary // This is only needed if you try to get an OkHttpClient without matching the name
    @BService
    fun httpClient(): OkHttpClient = OkHttpClient()
    @BService
    fun cachedHttpClient(
        // Inject the default http client (declared above)
        // This is not the same as calling the method! as it would create 2 different clients
        // This would not work if the parameter was named differently,
        // unless @Primary was used on the default declaration above
        httpClient: OkHttpClient
    ): OkHttpClient {
        val tempDirectory = Files.createTempDirectory(null).toFile()
        return httpClient.newBuilder()
            .cache(Cache(tempDirectory, maxSize = 1024 * 1024))
            .build()
    }
}
@BService
class MyApi(@ServiceName("cachedHttpClient") httpClient: HttpClient)
@BService
class MyApi(private val cachedHttpClient: HttpClient)
@BService
public class HttpClientProvider {
//    @Primary // This is only needed if you try to get an OkHttpClient without matching the name
    @BService
    public OkHttpClient httpClient() {
        return new OkHttpClient();
    }
    @BService
    public OkHttpClient cachedHttpClient(
            // Inject the default http client (declared above)
            // This is not the same as calling the method! as it would create 2 different clients
            // This would not work if the parameter was named differently,
            // unless @Primary was used on the default declaration above
            OkHttpClient httpClient
    ) throws IOException {
        final File tempDirectory = Files.createTempDirectory(null).toFile();
        return httpClient.newBuilder()
                .cache(new Cache(tempDirectory, 1024 * 1024))
                .build();
    }
}
@BService
public class MyApi {
    public MyApi(@ServiceName("cachedHttpClient") HttpClient httpClient) {
        // ...
    }
}
@BService
public class MyApi {
    public MyApi(HttpClient cachedHttpClient) {
        // ...
    }
}
Warning
For this to work, you need to enable Java parameter names
Primary providers¶
When requesting a service of a specific type/name, there must be at most one usable service provider.
For example, if you have two service factories with the same return type:
- If both are usable 
- One has a failing condition, meaning you have one usable provider 
- One is annotated with - @Primary, in which case this one is prioritized
Note
You can still retrieve existing services with ServiceContainer#getInterfacedServices/getInterfacedServiceTypes
Interfaced services¶
A list which the element type is an interfaced service can be requested, the list will then contain all instantiable instances with the specified type.
Example
List<ApplicationCommandFilter<?>> will contain all instances implementing ApplicationCommandFilter, 
which are usable.
Lazy services¶
Lazy service retrieval enables you to get lazily-created service, delaying the initialization,
or to get services that are not yet available, such as manually injected services (like JDA).
Retrieving a lazy service
Request a Lazy with the element type being the requested service, 
and then get the service when needed by using Lazy#getValue.
Request a ServiceContainer and use a delegated property, such as:
private val helpCommand: IHelpCommand by serviceContainer.lazy()
Note
Lazy injections cannot contain a list of interfaced services, nor can a list of lazy services be requested.
Optional services¶
When a requested service is not available, and is not a soft-dependency, service creation will fail.
In case your service does not always require the service,
you can prevent failure by using Kotlin's nullable / optional parameters,
but Java users will need a runtime-retained @Nullable annotation 
(such as @javax.annotation.Nullable, or, in checker-framework or JSpecify) or @Optional.
Lazy nullability
Lazy services can also have their element type be marked as nullable, 
for example, Lazy<@Nullable IHelpCommand>.