Java Enum definition

后端 未结 7 554
孤独总比滥情好
孤独总比滥情好 2020-11-22 17:24

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

class Enum>


        
相关标签:
7条回答
  • 2020-11-22 17:31

    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”.

    0 讨论(0)
  • 2020-11-22 17:35

    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<SELF extends Node<SELF>>{
        String name;
    
        SELF setName(String name) {
            this.name = name;
            return self();
        }
    
        protected abstract SELF self();
    }
    
    class City extends Node<City> {
        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!
        }
    }
    
    0 讨论(0)
  • 2020-11-22 17:44

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

    public abstract class Enum<E extends Enum<E>>
            implements Comparable<E>, Serializable {
    
        public final int compareTo(E o) {
            Enum<?> other = (Enum<?>)o;
            Enum<E> 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<E> getDeclaringClass() {
            Class<?> clazz = getClass();
            Class<?> zuper = clazz.getSuperclass();
            return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
        }
    
        public static <T extends Enum<T>> T valueOf(Class<T> 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<E> 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<MyEnum> {
        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<MyEnum> 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<MyEnum> getDeclaringClass() {
            Class<?> clazz = getClass();
            Class<?> zuper = clazz.getSuperclass();
            return (zuper == Enum.class) ? (Class<MyEnum>)clazz : (Class<MyEnum>)zuper;
        }
    

    This makes getDeclaringClass() cast to the proper Class<T> 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.

    0 讨论(0)
  • 2020-11-22 17:46

    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<Season>. So we're going to need

    Season extends ... implements Comparable<Season>
    

    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<Season>, so that you can have:

    Season extends Enum<Season>
    Enum<Season> implements Comparable<Season>
    

    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<E>
    

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

    0 讨论(0)
  • 2020-11-22 17:53

    According to wikipedia, this pattern is called Curiously recurring template pattern. Basically, by using the CRTP pattern, we can easily refer to subclass type without type casting, which means by using the pattern, we can imitate virtual function.

    0 讨论(0)
  • 2020-11-22 17:54

    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<T extends Node<T>>
    {
        public void addNeighbor(T);
    
        public void addNeighbors(Collection<? extends T> nodes);
    
        public Collection<T> getNeighbor();
    }
    

    Then you can have graphs of specialized types:

    public class City extends Node<City>
    {
        public void addNeighbor(City){...}
    
        public void addNeighbors(Collection<? extends City> nodes){...}
    
        public Collection<City> getNeighbor(){...}
    }
    
    0 讨论(0)
提交回复
热议问题