1

I have two methods:

   public <T extends Component> void addComponent(BluePrint bluePrint, Class<T> type) throws InstantiationException, IllegalAccessException {
        AddComponent addComponent = addComponentMap.get(type);
        if (addComponent == null) {
            addScriptable(bluePrint, type); <--- fails here
        }
    }

if addComponentMap.get(type); returns null, i know implicitely that T is of type Scriptabe and need to call:

    private <T extends Scriptable> void addScriptable(BluePrint bluePrint, Class<T> type) throws InstantiationException, IllegalAccessException {
        scriptableSystems.add(new ScriptableSystem<T>());
    }

The issue is that the upper bound for T in the second method is Scriptable and in the first method its Component, therefore type "could" potentially be any component when addComponent is null.

Can i somehow narrow the constraint to Scritpable when addComponent is null? Or somehow explicitly say that when addComponent is null T will extend Scriptable, before calling addScriptable?

Worth mentioning perhaps is that Scriptable inherits from component.

4
  • 1
    Are the other components in the map also Scriptable? In that case, make T extends Scriptable in the first one, too. Otherwise, you could just cast if you definitely know. If you cannot guarantee that all components in the map are Scriptable, I'd say you are out of luck using only generics. Commented Oct 16, 2017 at 14:56
  • Perhaps something like this. Commented Oct 16, 2017 at 14:57
  • @MalteHartwig The other components in the map are not Scriptable. How do i cast the variable type? What other methods could i use? Commented Oct 16, 2017 at 15:09
  • @Tagor, I wrote an answer explaining how Class.isAssignableFrom() could help Commented Oct 16, 2017 at 15:10

2 Answers 2

1

The issue is that the upper bound for T in the second method is Scriptable and in the first method its Component, therefore type "could" potentially be any component when addComponent is null.

That's about right, but I'd put it more strongly: since Scriptable extends Component (as opposed to the other way around), type argument T in method addComponent() can always be a type that is not bounded above by Scriptable.

Can i somehow narrow the constraint to Scritpable when addComponent is null?

Sure. Supposing that you don't want a tighter bound in other cases, that's what casting is for:

public <T extends Component> void addComponent(BluePrint bluePrint, Class<T> type)
        throws InstantiationException, IllegalAccessException {
    AddComponent addComponent = addComponentMap.get(type);

    if (addComponent == null) {
        addScriptable(bluePrint, (Class<? extends Scriptable>) type);
    }
}

You will of course get a compiler warning about the cast. This is right and proper because that code depends on a condition that the compiler cannot verify.

Or somehow explicitly say that when addComponent is null T will extend Scriptable, before calling addScriptable?

That's exactly what you do say by casting. A cast of a value of reference type is effectively an assertion that you know more about the runtime type of that value than the compiler can prove.

Additionally, you can perform a runtime test if you'd rather have a filaure behavior different from a ClassCastException:

if (!Scriptable.class.isAssignableFrom(type)) {
    throw new MyChosenException();
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks! Very detailed answer indeed.
0

If you cannot narrow down the generic type of class in your map, you can still cast. And to be safer, you can use Class.isAssigneableFrom, which is an instanceof for classes (instead of objects):

public static void main(String[] args) {
    Map<String, Class<? extends Number>> map = new HashMap<>();
    Class<? extends Number> someNumberClass = map.get("Double");
    if (Integer.class.isAssignableFrom(someNumberClass)) {
        acceptInteger((Class<Integer>) someNumberClass);
    }
}

public static void acceptInteger(Class<Integer> c) { }

This doesn't even need the null check and could work for cases where addComponent is not null. In your example, you would do:

if (Scriptable.class.isAssignableFrom(type)) { // add the null check if necessary
   addScriptable(bluePrint, (Class<Scriptable>) type);
}

2 Comments

@Tagor For a more detailed explanation, please read John Bollinger's extensive answer to this question.
Nice! never heard of isAssigneableFrom, thanks for sharing :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.