Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confused about the generic which extends an exist Enum type

QUESTION 1: I am a bit confused about these codes:

public class MyClass1 <E extends Enum<?>> {
    // ...
}
public class MyClass2 <E extends Enum<E>> {
    // ... 
}

What is the different between MyClass1 and MyClass2, and what do the three different E mean?

QUESTION 2: From Class Enum <E extends Enum<E>>

This is the common base class of all Java language enumeration types.

But isn't the common base class of all Enum type Enum<E>?

QUESTION 3: right now I have this class:

public class MyClass<E extends Enum<E>> {

    // for example, EnumSet<Category> 
    public Set<E> category; 

    // how to initial this member
    private Class<E> enumType; 

    // initial this.category with given strings
    public void getEnumSetFromStringList(List<String> list) {
        this.category = EnumSet.noneOf(enumType);
        for (String str : list) {
            this.category.add(Enum.valueOf(this.enumType, str));
        }
    }
}

// this is the Category of Enum type
public enum Category {

    LOCATION("LOCATION"), 
    HUMAN("HUMAN"),
    // ...
    DESCRIPTION("DESCRIPTION");

    private final String categoryName;

    Category(String categoryName) {
        this.categoryName= categoryName;
    }
}
  1. How could I initial the data field enumType in MyClass?

  2. If MyClass does not contain the data field enumType, how could the function getEnumSetFromStringList(List<String> list) get the type of the generic type <E extends Enum<?>>?

  3. Can I get the Enum type in this way: E.class? If not, is it reason from the type-erasure of generic type during compile?

like image 318
stanleyerror Avatar asked Dec 05 '25 10:12

stanleyerror


1 Answers

Enum<E extends Enum<E>> is very difficult to understand.

Let's consider two enums.

enum Colour { RED, GREEN, BLUE }

enum Shape { SQUARE, CIRCLE, TRIANGLE }

Think about the compareTo() method in Enum. This is used to compare different constants in the same Enum based on the order they are declared in. If Enum were not a generic class, the signature for this method would have to be int compareTo(Enum e). But that wouldn't make any sense because then you could compare a Colour to a Shape. We want the signature of compareTo in Colour to be int compareTo(Colour colour). The only way to do that is if Enum is a generic class with a type paramter E and the type parameter for Colour is Colour! As long as the type parameter of Colour is Colour and the type parameter of Shape is Shape then we can only compare Colours to Colours and Shapes to Shapes.

So in the definition of Enum we want some way of expressing that, for every subclass, the type parameter must not only be an enum but the type parameter should be the subclass itself. Therefore E should not just extend Enum but E should extend Enum<E>!

This is where Enum<E extends Enum<E>> comes from.

(You should not write Enum<E extends Enum<?>>).

One way to assign a value to enumType is to pass a Class<E> to the constructor. Like this

class MyClass2<E extends Enum<E>> {
    private final Class<E> enumType;

    MyClass2(Class<E> enumType) {
        this.enumType = enumType;
    }
}

If you don't want to do that, another way to get the Class<E> object at runtime is to use the Enum instance method getDeclaringClass(). This requires you to have an instance of E at hand to call the method on though. One way of getting an array of all enum constants at runtime is to write e.getDeclaringClass().getEnumConstants().

You cannot write E.class because, as you rightly say, generics are implemented using the process of type erasure. At runtime, an ArrayList<String> is just an ArrayList, so it is impossible to access the type parameter.

like image 147
Paul Boddington Avatar answered Dec 06 '25 22:12

Paul Boddington



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!