I'm currently working on a component-based architecture management system in java. My current implementation of the retrieval of a component attached to an object works like this:
// ...
private final HashMap<Class<? extends EntityComponent>, EntityComponent> components;
// ...
public <T extends EntityComponent> T getComponent(Class<T> component)
{
// ... some sanity checks
if (!this.hasComponent(component))
{
// ... some exception handling stuff
}
return component.cast(this.components.get(component));
}
// ...
Now, this works fine, but it somewhat bugs me to have to write
object.getComponent(SomeComponent.class)
everytime I need to access a component.
Would it be possible to utilize generics in a way to shift the syntax to something more along the lines of
object.getComponent<SomeComponent>()
, utilizing the diamond operator to specify the class, instead of passing the class of the component as a parameter to the method?
I know it's not really a big thing, but making the syntax of often used code as pretty / compact as possible goes a long way I guess.
Unfortunately not, since type-parameters are "erased" in Java. That means that they are only available at compile-time (where the compiler is using them to type-check the code), but not at run-time.
So when your code is running, the <SomeComponent>
type-parameter no longer exists, and your code therefore can't do any operations (if/else, etc) based on its value.
In other words:
object.getComponent<SomeComponent>()
object.getComponent()
. There is no type-parameter any more.So, yes, unfortunately you still need to pass a Class
object along, or something similar (see "Super Type Tokens" for example), if you need to do something that depends on the type parameter at run-time.
The reason the Class
workaround works is that it loosely speaking represents the type-parameter, since the type-checker makes sure that its instance fits with the type-parameter, but is an object and thus available at run-time too - unlike the type-parameter.
Note: The Class
-trick doesn't work for type-parameters within type-parameters, such as Class<List<Something>>
, since at run-time List<Something>
and List<OtherThing>
is the same class, namely List
. So you can't make a Class
token to differentiate between those two types. As far as i remember "Super Type Tokens" can be used instead to fix this (they exploit the fact that there is an exception to erasure: For subclasses of generic classes, the type-parameters used when "extending" the superclass are actually available at run-time through reflection. (there are also more exceptions: https://stackoverflow.com/a/2320725/1743225)).
(Related google terms: "Erasure", "Reification", "Reified generics")