0

I have an enum in my class which contains an Class attribute, as so:

public enum DatEnum {
  CONSTANT_ONE(ClassOne),
  CONSTANT_TWO(ClassTwo);

  private Class<? extends UpperBoundClass> classAttribute;

  private DatEnum(Class<? extends UpperBoundClass> classAttribute) {
    this.classAttribute = classAttribute;
  }

  public <T extends UpperBoundClass> T getInstance() {
    UpperBoundClass instance = classAttribute.newInstance();
    return instance;
  }
}

But then I get a warning in the first line of the getter:

Unchecked cast from capture#4-of ? extends UpperBoundClass to T

And also in the second line of the getter:

Unchecked cast from UpperBoundClass to T

So far the code works, but i'd really like to get rid of the warning as well as to have a better understanding of what the problem actually is here.

4
  • 1
    You have no enum constants there. Commented Sep 26, 2014 at 21:42
  • Also, do you mean newInstance? Commented Sep 26, 2014 at 21:43
  • Technically, all you need is a semi-colon above the field declaration, but it's really weird that you could do something like that. Commented Sep 26, 2014 at 22:02
  • Yes @SotiriosDelimanolis, i meant newInstance, the question is updated now, i also added enum constants. Commented Sep 26, 2014 at 22:25

2 Answers 2

1
public <T extends UpperBoundClass> T getInstance() {
  UpperBoundClass instance = classAttribute.newInstance();
  return instance;
}

This code does not actually return <T>. It returns an instance of the specific Class<? extends UpperBoundClass> referenced by the classAttribute field. All the compiler can verify is that instance is assignable to (able to be cast to) UpperBoundClass. However, there is no declared relationship between that instance and the specific type represented by <T> in the method signature.

End result: you're going to get a warning. It might happen to work as long as every place you use CONSTANT_ONE or CONSTANT_TWO is already expecting the same type as you put in their respective constructors, but the compiler can't enforce or know that.

If you change your method to the following, then you will see the usage sites (in the calling code) that are not strictly type-safe because they assume a particular subclass:

public UpperBoundClass getInstance() {
  return classAttribute.newInstance();
}

Alternatively, you can provide some relationship between your <T> and the stored class reference:

public <T extends UpperBoundClass> T getInstance(Class<T> type) {
  UpperBoundClass instance = classAttribute.newInstance();
  return type.cast(instance); // verifies type-safety
}

But there's little point in doing that, because the caller could either call type.newInstance() itself or simply new ClassOne() (depending on the use case). The call to type.cast(instance) could throw ClassCastException at runtime, but the compiler will be happy because it considers it your problem at that point.

You're fighting the fact that an enum presents a uniform contract; and while you can customize individual constants with polymorphic behavior, you cannot use generics to parameterize the enum's base API with something that varies from instance to instance.

Related reading: Why shouldn't Java enum literals be able to have generic type parameters?

Sign up to request clarification or add additional context in comments.

2 Comments

Thank you William! I just changed getInstance() to what you wrote and no usage sites were type-unsafe. I'm wondering though, will getInstance() then return an instance of UpperBoundClass or an instance of the subclass specified in the constructor?
Class.newInstance() always returns an instance of the exact type represented by the Class instance. In your question, that's ClassOne.class for CONSTANT_ONE and ClassTwo.class for CONSTANT_TWO. However, it appears in the API to be nothing more specific than UpperBoundClass and callers cannot depend on any API that's not available in the upper bound without downcasting (and potentially getting a ClassCastException). As long as your callers only need to use methods on UpperBoundClass, you'll be fine.
0

First of all,your enum class does not have enum constants.Secondly,I believe that your Enum Object will have Class Object as its memeber variables.In most of the cases,you will have a single Object for each class(unless you are using multiple classLoaders for loading a class),so why not use a simple data structure containing only Class Objects.

As it is ,you can always use a Class.newInstance() or Class.getDeclaredConstructors() to get the Constructor and sue it to have your own Instance.

1 Comment

I'm not sure i understand this correctly but the idea here is to have an upper bound to the Class Object member variable to provide some sort of warranty regarding what the actual instance can accomplish.

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.