Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning a casted object based on input Enum value

I am trying to make a method that takes an Enum value and returns an object that is casted to a class based on that Enum value. For example, I have an Enum called ComponentType:

public enum ComponentType
{
    HEALTH(HealthComponent.class),
    HUNGER(HungerComponent.class);

    private Class<? extends Component> componentClass;

    private ComponentType(Class<? extends Component> componentClass)
    {
        this.componentClass = componentClass;
    }

    public Class<? extends Component> getComponentClass()
    {
        return componentClass;
    }
}

"HealthComponent" and "HungerComponent" are two classes that both extend a class called "Component". What is inside them is not important for this question.

An Entity would have a list of Components that they are assigned (For example, one entity may have hunger, while another may have health, and another may have both).

My objective is to create a method inside the Entity that when a value from the ComponentType Enum is passed in, a Component object casted to the corresponding class type for that value is returned. So if you passed in ComponentType.HEALTH, the method would return an object casted to HealthComponent. Here is what I am trying, but it is not working: (Edit: see below)

public <T extends Component> T getComponentByType(ComponentType type)
{
    Class<? extends Component> componentClass = type.getComponentClass();
    for(Component component : componentList)
    {
        if(component.getClass() == componentClass)
        {
            return (T) componentClass.cast(component);
        }
    }
    return null;
}

For the above method, when passing in a type of ComponentType.HEALTH:

entity.getComponentByType(ComponentType.HEALTH);

an object casted to "Component" rather than "HealthComponent" would be returned. I want it to return an object casted to HealthComponent, not Component.

Is there any way to do this? I feel like this should be possible. My reason for trying to find a way to do this is because it seems like doing all this casting:

HealthComponent component = (HealthComponent) entity.getComponentByType(ComponentType.HEALTH);

is a bit wasteful since the method could (hopefully) assume what I want to cast to by the ComponentType passed in.

Edit (More info):

Taking a closer look at the results, I noticed that my method above some-what works. What I mean by this is that in Eclipse, if I type out:

component = entity.getComponentByType(ComponentType.HEALTH);

(the component variable is not defined yet) and then hover over getComponentByType to see what it is returning, it says it is returning <Component> Component

However, if I manually define the variable type (most of the time I just let Eclipse create the variables for me) like this:

HealthComponent component = entity.getComponentByType(ComponentType.HEALTH);

And then hover over getComponentByType to see what it is returning, it says it is returning <HealthComponent> HealthComponent and it does compile and run. So it technically works, but not in the way I'd like it to. This is a small issue because if I tell Eclipse to create a local variable for me in the first example, it would create a variable of type "Component" which I would have to manually change.

like image 947
Mitch Talmadge Avatar asked Oct 31 '22 15:10

Mitch Talmadge


1 Answers

You want the compile-time return type of getComponentByType() to depend on the parameter that you use to select the component. This is possible with generics, but only if the parameter actually carries the compile-time type information that you need.

Unfortunately, you can't add type parameters to enum values (see this question), but if you look at your own code again, you might realize that you already have an object that fittingly describes which component you want to get, and that happens to carry the type information we need: the Class<> object of each component type!

So, here's my suggestion for your function (not compiled or tested, beware, my Java might be rusty):

public <T extends Component> T getComponentByType(Class<T> type)
{
    for(Component component : componentList)
    {
        if(component.getClass() == type)
        {
            return (T)component;
        }
    }
    return null;
}
like image 197
Medo42 Avatar answered Nov 09 '22 15:11

Medo42