I have a bunch of class properties that are initialized in a somewhat complicated but very uniform way. Since it is both error prone and a pain to maintain about 10 copies of this code I would like to abstract this.
Here is what the code looks like for setting one property:
if ( userConfigMapPath != null ) {
final TypeReference<UserConfig> ref =
new TypeReference<UserConfig>() {
};
this.userConfig = Optional.of(
new GenericConfigMap<>( userConfigMapPath, (int) REFRESH_RATE.getSeconds(), objectMapper, ref ) );
} else {
this.userConfig = Optional.empty();
}
This is repeated for each property, except with differing property names (in this case userConfig), path (userConfigMapPath) and type (userConfigMap).
So I try abstracting it into a method.
private <T> Optional<GenericConfigMap<T>> optionalGenericConfigMapFromPath( final ObjectMapper objectMapper, final String path ) {
Optional<GenericConfigMap<T>> result;
if ( path != null ) {
final TypeReference<T> ref =
new TypeReference<T>( ) {
};
result = Optional.of(
new GenericConfigMap<>( path, (int) REFRESH_RATE.getSeconds( ), objectMapper, ref ) );
} else {
result = Optional.empty( );
}
return result;
}
Note that since the type of each property is different this function is generic. I use this method to set each property:
this.userConfig = optionalGenericConfigMapFromPath(objectMapper, userConfigMapPath);
this.networkConfig = optionalGenericConfigMapFromPath(objectMapper, networkConfigMapPath);
this.dodadConfig = optionalGenericConfigMapFromPath(objectMapper, dodadConfigMapPath);
Now this compiles however it has some serious runtime issues. The crux of the issue is that the compiler seems unable to determine what T is in any of the above cases. It seems to base its selection of T based on the default of the object mapper instead of the type of the property. And since the object mapper is reading from json this makes for a LinkedHashMap instead of any type we want.
Now this doesn't cause a compile error, something that baffles me, but instead produces an exception any time we try to use a method from one of these properties we get a runtime type error (could not cast LinkedHashMap to the actual type).
What I want is for the compiler to actually know at compile time the type of T for each call to the function. In fact I would be perfectly happy to tell it the type of T if it would let me. Using this syntax that @JeroenSteenbeeke helped me with
this.userConfig = this.<UserConfig> optionalGenericConfigMapFromPath(objectMapper, userConfigMapPath);
this.networkConfig = this.<NetworkConfig> optionalGenericConfigMapFromPath(objectMapper, networkConfigMapPath);
this.doodadConfig = this.<DoodadConfig> optionalGenericConfigMapFromPath(objectMapper, doodadConfigMapPath);
This type information seems to have just been ignored since when I run the debugger it still behaves exactly as it did. So I tried some janky dependent typing using Class as a proxy:
private <T> Optional<GenericConfigMap<T>> optionalGenericConfigMapFromPath( final Class<T>, final ObjectMapper objectMapper, final String path ) {
Optional<GenericConfigMap<T>> result;
if ( path != null ) {
final TypeReference<T> ref =
new TypeReference<T>( ) {
};
result = Optional.of(
new GenericConfigMap<>( path, (int) REFRESH_RATE.getSeconds( ), objectMapper, ref ) );
} else {
result = Optional.empty( );
}
return result;
}
this.userConfig = optionalGenericConfigMapFromPath( UserConfig.class, objectMapper, userConfigMapPath);
this.networkConfig = optionalGenericConfigMapFromPath( NetworkConfig.class, objectMapper, networkConfigMapPath);
this.doodadConfig = optionalGenericConfigMapFromPath( DoodadConfig.class, objectMapper, doodadConfigMapPath);
This is basically the dependent function type:
The compiler also ignores the type information given in this example and continues to produce the same incorrectly typed programs as before.
I'm really at my wits end with this. Is there any way to get the compiler to recognize this type information and create values of the correct types?
