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
inMyClass
? -
If it
MyClass
doesn't contain a data fieldenumType
, how could a functiongetEnumSetFromStringList(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?
source to share
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.
source to share
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.
source to share