I currently working on a JPMS project and would like to be able to cache the Providers retrieved on ServiceLoader.load() to be able to use their get() method later for a new set of service instances - without a reference to the ServiceLoader necessarily. In short, retrieve a collection of them from some map and return them as ServiceLoader.Provider<.T>
Even shorter, how do I do:
public static <T> List<ServiceLoader.Provider<T>> getProvidersOf(Class<T> clazz){
return (List<ServiceLoader.Provider<T>>) servicesProvidersMap.get(clazz);
}
For a map: Map<Class<?>, List<ServiceLoader.Provider<?>>> servicesProvidersMap = new ConcurrentHashMap<>();
What have I tried:
- Cast it to object then back to Provider<.T>
- Used the iterator, but that returns new T instances, not Provider<.T>
- Not using the streams api. However, "for" uses the iterator.
What does work, but isn't what I'm looking for:
- Individually casting each provider when adding them to a new list instance. But that is not in line with "having the value pre-calculated and ready to go".
.
public static <T> List<ServiceLoader.Provider<T>> getProvidersOf(Class<T> clazz){
List<ServiceLoader.Provider<?>> cached = servicesProvidersMap.get(clazz);
if(cached != null) {
List<ServiceLoader.Provider<T>> casted = new CopyOnWriteArrayList<>();
cached.forEach(provider -> casted.add((ServiceLoader.Provider<T>) provider));
return casted;
}
[...on empty cache]
}
- Caching the loader and re-retrieving the Providers every call. Same as above for my purposes really.
.
List<ServiceLoader.Provider<T>> hereWeGoAgain = getLoaderFor(clazz)
.stream()
.collect(Collectors.toCollection(CopyOnWriteArrayList::new));
I understand that a ServiceLoader.Provider instance may be some transition object, and not intended to be kept around, however, it would provide me a great deal of options and flexibility if it was possible.
You can create a helper class to hold the map of providers and encapsulate the necessary casting logic,
here
addProvidermethod puts providers into the map ensuring they have the correct type. When you retrieve providers withgetProvidersOf, it performs the unchecked cast, which is safe because of the way providers were added.This approach minimizes unchecked casts and confines them to a single place, ensuring type safety and reducing potential issues caused by incorrect casting. However, be aware that this class is not thread-safe. If multiple threads might be adding and retrieving providers at the same time, you would need to add appropriate
synchronization.