Confused by a generic type that extends an existing Enum type

QUESTION 1 . I am a little confused with these codes:

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

      

What is the difference between MyClass1

and MyClass2

, and what do the three different mean E

?

QUESTION 2 : FromClass Enum <E extends Enum<E>>

It is a common base class for all Java enumeration types.

But isn't it a common base class for all Enum types Enum<E>

?

QUESTION 3 : I now 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;
    }
}

      

  • How could I initialize a data field enumType

    in MyClass

    ?

  • If it MyClass

    doesn't contain a data field enumType

    , how could a function getEnumSetFromStringList(List<String> list)

    get a generic type <E extends Enum<?>>

    ?

  • Can I get an Enum type this way E.class

    :? If it is not, is this the reason for the type-erasure of the generic type at compile time?

+3


source to share


2 answers


Enum<E extends Enum<E>>

very difficult to understand.

Consider two enums

.

enum Colour { RED, GREEN, BLUE }

enum Shape { SQUARE, CIRCLE, TRIANGLE }

Think about the method compareTo()

in Enum

. This is used to compare different constants in the same one Enum

depending on the order in which they are declared. If Enum

not a generic class, the signature for this method must be int compareTo(Enum e)

. But that doesn't make any sense, because then you could compare Colour

with a Shape

. We want the signature compareTo

to Colour

be int compareTo(Colour colour)

. The only way to do this is if Enum

is a generic class with a type E

parameter and a type parameter for Colour

- Colour

! As long as the type parameter Colour

is equal Colour

and the type parameter Shape

is equal Shape

, we can only compare Colour

with Colour

and Shape

with Shape

s.

So in the definition, Enum

we want some way to express that for each subclass, the type parameter must not only be Enum

, but the type parameter must be the subclass itself. Therefore, E

it should not just expand Enum

, but E

should expand Enum<E>

!



Here Enum<E extends Enum<E>>

.

(You don't have to write Enum<E extends Enum<?>>

).

One way to assign a value enumType

is to pass it to a Class<E>

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 this, another way to get an object Class<E>

at runtime is to use an instance method Enum

getDeclaringClass()

. This requires that you have an instance E

to call the method. One way to get an array of all enum constants at runtime is to write e.getDeclaringClass().getEnumConstants()

.

You cannot write E.class

, because as you correctly say, generics are implemented with a type erasure process. At run time, it ArrayList<String>

is only ArrayList

, so it is impossible to access the type parameter.

+4


source


What is the difference between MyClass1 and MyClass2 and what do the three different E's mean?

MyClass1

introduces a generic type parameter called E

, which extends the class Enum<?>

. This ?

means we don't know (and care) which generic version of the class Enum

extends E

, so a wildcard is used.

Meanwhile MyClass2

also introduces a generic type called E

, which expands the type again Enum

. At this time, the type Enum

is common in the same E

, which means that the type parameter must be a subclass of class Enum

and will also support all methods defined by Enum

that use the same type E

. For example, a method getDeclaringClass()

that returns Class<E>

instead of Class<Object>

.


But isn't it a common base class for all types of Enum Enum?

Not. A generic parameter is meaningless if it is not a subclass Enum<E>

.


How can I initialize the enumType field of a data field in MyClass?



You have to pass Class<E>

in the constructor and copy the value to the private member:

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

      


If MyClass does not contain the data enumType field, how does getEnumSetFromStringList (...) function get the generic type?

He will not be able to receive it due to type erasure. If you want a generic type class, you will need to support the element that contains the value Class<E>

;


Can I get the Enum type this way: E.class? If not, is this the reason for the type erasure of the generic type at compile time?

You can't, because E

- it's just an alias for what will be Runtime specific.

+3


source







All Articles