Java Enum definition

匿名 (未验证) 提交于 2019-12-03 01:14:02

问题:

I thought I understood Java generics pretty well, but then I came across the following in java.lang.Enum:

class Enum> 

Could someone explain how to interpret this type parameter? Bonus points for providing other examples of where a similar type parameter could be used.

回答1:

It means that the type argument for enum has to derive from an enum which itself has the same type argument. How can this happen? By making the type argument the new type itself. So if I've got an enum called StatusCode, it would be equivalent to:

public class StatusCode extends Enum

Now if you check the constraints, we've got Enum - so E=StatusCode. Let's check: does E extend Enum? Yes! We're okay.

You may well be asking yourself what the point of this is :) Well, it means that the API for Enum can refer to itself - for instance, being able to say that Enum implements Comparable. The base class is able to do the comparisons (in the case of enums) but it can make sure that it only compares the right kind of enums with each other. (EDIT: Well, nearly - see the edit at the bottom.)

I've used something similar in my C# port of ProtocolBuffers. There are "messages" (immutable) and "builders" (mutable, used to build a message) - and they come as pairs of types. The interfaces involved are:

public interface IBuilder   where TMessage : IMessage    where TBuilder : IBuilder  public interface IMessage   where TMessage : IMessage    where TBuilder : IBuilder

This means that from a message you can get an appropriate builder (e.g. to take a copy of a message and change some bits) and from a builder you can get an appropriate message when you've finished building it. It's a good job users of the API don't need to actually care about this though - it's horrendously complicated, and took several iterations to get to where it is.

EDIT: Note that this doesn't stop you from creating odd types which use a type argument which itself is okay, but which isn't the same type. The purpose is to give benefits in the right case rather than protect you from the wrong case.

So if Enum weren't handled "specially" in Java anyway, you could (as noted in comments) create the following types:

public class First extends Enum {} public class Second extends Enum {} 

Second would implement Comparable rather than Comparable... but First itself would be fine.



回答2:

The following is a modified version of the explanation from the book Java Generics and Collections: We have an Enum declared

enum Season { WINTER, SPRING, SUMMER, FALL } 

which will be expanded to a class

final class Season extends ... 

where ... is to be the somehow-parameterised base class for Enums. Let's work out what that has to be. Well, one of the requirements for Season is that it should implement Comparable. So we're going to need

Season extends ... implements Comparable

What could you use for ... that would allow this to work? Given that it has to be a parameterisation of Enum, the only choice is Enum, so that you can have:

Season extends Enum Enum implements Comparable

So Enum is parameterised on types like Season. Abstract from Season and you get that the parameter of Enum is any type that satisfies

 E extends Enum

Maurice Naftalin (co-author, Java Generics and Collections)



回答3:

You are not the only one wondering what that means; see Chaotic Java blog.

“If a class extends this class, it should pass a parameter E. The parameter E’s bounds are for a class which extends this class with the same parameter E”.



回答4:

This post has totally clarified to me these problem of 'recursive generic types'. I just wanted to add another case where this particular structure is necessary.

Suppose you have generic nodes in a generic graph:

public abstract class Node> {     public void addNeighbor(T);      public void addNeighbors(Collection nodes);      public Collection getNeighbor(); } 

Then you can have graphs of specialized types:

public class City extends Node {     public void addNeighbor(City){...}      public void addNeighbors(Collection nodes){...}      public Collection getNeighbor(){...} } 


回答5:

This can be illustrated by a simple example and a technique which can be used to implement chained method calls for sub-classes. In an example below setName returns a Node so chaining won't work for the City:

class Node {     String name;      Node setName(String name) {         this.name = name;         return this;     } }  class City extends Node {     int square;      City setSquare(int square) {         this.square = square;         return this;     } }  public static void main(String[] args) {     City city = new City()         .setName("LA")         .setSquare(100);    // won't compile, setName() returns Node } 

So we could reference a sub-class in a generic declaration, so that the City now returns the correct type:

abstract class Node>{     String name;      SELF setName(String name) {         this.name = name;         return self();     }      protected abstract SELF self(); }  class City extends Node {     int square;      City setSquare(int square) {         this.square = square;         return self();     }      @Override     protected City self() {         return this;     }      public static void main(String[] args) {        City city = new City()             .setName("LA")             .setSquare(100);                 // ok!     } } 


回答6:

In the case of Enum, it is useless. Everything would work the same if it were declared as

class Enum


回答7:

If you look at the Enum source code, it has the following:

public abstract class Enum>         implements Comparable, Serializable {      public final int compareTo(E o) {         Enum other = (Enum)o;         Enum self = this;         if (self.getClass() != other.getClass() && // optimization             self.getDeclaringClass() != other.getDeclaringClass())             throw new ClassCastException();         return self.ordinal - other.ordinal;     }      @SuppressWarnings("unchecked")     public final Class getDeclaringClass() {         Class clazz = getClass();         Class zuper = clazz.getSuperclass();         return (zuper == Enum.class) ? (Class)clazz : (Class)zuper;     }      public static > T valueOf(Class enumType,                                                 String name) {         T result = enumType.enumConstantDirectory().get(name);         if (result != null)             return result;         if (name == null)             throw new NullPointerException("Name is null");         throw new IllegalArgumentException(             "No enum constant " + enumType.getCanonicalName() + "." + name);     }  } 

First thing first, what does E extends Enum mean? It means the type parameter is something that extends from Enum, and isn't parametrized with a raw type (it's parametrized by itself).

This is relevant if you have an enum

public enum MyEnum {     THING1,     THING2; } 

which, if I know correctly, is translated to

public final class MyEnum extends Enum {     public static final MyEnum THING1 = new MyEnum();     public static final MyEnum THING2 = new MyEnum(); } 

So this means that MyEnum receives the following methods:

public final int compareTo(MyEnum o) {     Enum other = (Enum)o;     Enum self = this;     if (self.getClass() != other.getClass() && // optimization         self.getDeclaringClass() != other.getDeclaringClass())         throw new ClassCastException();     return self.ordinal - other.ordinal; } 

And even more importantly,

    @SuppressWarnings("unchecked")     public final Class getDeclaringClass() {         Class clazz = getClass();         Class zuper = clazz.getSuperclass();         return (zuper == Enum.class) ? (Class)clazz : (Class)zuper;     } 

This makes getDeclaringClass() cast to the proper Class object.

A way clearer example is the one that I answered on this question where you cannot avoid this construct if you want to specify a generic bound.



文章来源: Java Enum definition
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!