EnumSet Generics puzzle

Discussion in 'Java' started by Roedy Green, Aug 18, 2005.

  1. Roedy Green

    Roedy Green Guest

    I am working on a an essay on EnumSet for the Java glossary.

    I wrote a little test program like this and works:

    -------------

    package com.mindprod.practice;

    public enum Breed { DALMATIAN, LABRADOR, DACHSHUND }

    --------------

    package com.mindprod.practice;

    import static com.mindprod.practice.Breed.*;
    import java.util.EnumSet;

    /**
    * test drive the EnumSet features
    */
    public class EnumSetTest
    {

    /**
    * main class
    *
    * @param args not used
    */
    public static void main ( String[] args )
    {
    // create a set by explicitly listing the individual elements
    EnumSet<Breed> breeds = EnumSet.of( DALMATIAN, DACHSHUND );
    System.out.println( breeds );

    }
    }

    -------------

    When I look at the code for EnumSet.of I find:

    /**
    * Creates an enum set initially containing the specified
    elements.
    *
    * Overloadings of this method exist to initialize an enum set
    with
    * one through five elements. A sixth overloading is provided
    that
    * uses the varargs feature. This overloading may be used to
    create an
    * an enum set initially containing an arbitrary number of
    elements, but
    * is likely to run slower than the overloadings that do not use
    varargs.
    *
    * @param e1 an element that this set is to contain initially
    * @param e2 another element that this set is to contain initially
    * @throws NullPointerException if any parameters are null
    * @return an enum set initially containing the specified elements
    */
    public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2) {
    EnumSet<E> result = noneOf(e1.getDeclaringClass());
    result.add(e1);
    result.add(e2);
    return result;
    }

    1. how come I don't have to put in:

    EnumSet<Breed> breeds = EnumSet<Breed>.of( DALMATIAN, DACHSHUND );

    I presume somehow it is inferring it from the types of the two parms.

    2. Why is the compiler rejecting it when I do?
     
    Roedy Green, Aug 18, 2005
    #1
    1. Advertising

  2. Roedy Green wrote:
    >
    > 1. how come I don't have to put in:
    >
    > EnumSet<Breed> breeds = EnumSet<Breed>.of( DALMATIAN, DACHSHUND );
    >
    > I presume somehow it is inferring it from the types of the two parms.


    Yup, it does that for methods, which is very useful. It doesn't do it
    for constructors, which is less helpful.

    You can exploit the method behaviour to write a class like:

    public class New {
    public static <K, V> java.util.HashMap<K,V> hashMap() {
    return new java.util.HashMap<K,V>();
    }

    So you can replace

    private final Map<String,String> myMap =
    new HashMap<String,String>();

    with

    private final Map<String,String> myMap = New.hashMap();

    > 2. Why is the compiler rejecting it when I do?


    [EnumSet<Breed> breeds = EnumSet<Breed>.of( DALMATIAN, DACHSHUND );]

    The generic parameterisation is on the method. Static methods and static
    fields are not effected by the class' parameters. There is only one
    class. So unfortunately you can't write, for instance:

    class <E> {
    private static List<E> emptyList = new EmptyList<E>();
    }

    It's generally a good idea to avoid reusing the same generic parameter
    name on static methods and nested classes. Otherwise you will cause
    confusion, as demonstrated. Particularly with instance methods and inner
    classes, where the compiler can tell you that E is not of type E (they
    are different Es).

    Back to the example. What you can write, if you so wish, is:

    EnumSet<Breed> breeds = EnumSet.<Breed>of( DALMATIAN, DACHSHUND );

    Note, you need the EnumSet. in front even if you have a static import.
    There's a similar syntax for constructors, but I've never seen it used.

    Tom Hawtin
    --
    Unemployed English Java programmer
    http://jroller.com/page/tackline/
     
    Thomas Hawtin, Aug 18, 2005
    #2
    1. Advertising

  3. Roedy Green

    Roedy Green Guest


    >
    >1. how come I don't have to put in:
    >
    >EnumSet<Breed> breeds = EnumSet<Breed>.of( DALMATIAN, DACHSHUND );
    >
    >I presume somehow it is inferring it from the types of the two parms.
    >
    >2. Why is the compiler rejecting it when I do?


    Similarly

    EnumSet<Breed> all = EnumSet.allOf( Breed.class );

    or

    EnumSet all = EnumSet.allOf( Breed.class );

    BOTH work, without error message. What gives? Do enum generics work by
    different rules?
     
    Roedy Green, Aug 18, 2005
    #3
  4. Roedy Green

    Roedy Green Guest

    On Thu, 18 Aug 2005 07:26:56 +0100, Thomas Hawtin
    <> wrote or quoted :

    >The generic parameterisation is on the method. Static methods and static
    >fields are not effected by the class' parameters. There is only one
    >class. So unfortunately you can't write, for instance:
    >
    >class <E> {
    > private static List<E> emptyList = new EmptyList<E>();
    >}


    I don't think that is the problem is this case. I quote Sun's code
    from EnumSet:

    public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2) {
    EnumSet<E> result = noneOf(e1.getDeclaringClass());
    result.add(e1);
    result.add(e2);
    return result;
    }

    Granted they do use E as the class type as well, but surely that E is
    not the same E as on the static, right?

    Then I try to call that static method:

    The compiler is happy with:
    EnumSet<Species> species1 = EnumSet.of( ASPEN, CEDAR );
    or
    EnumSet species1 = EnumSet.of( ASPEN, CEDAR );
    which in my opinion is a bug since species1 is then typeless and will
    let any EnumSet in.

    Presumably the compiler is deducing the type of E on its own. I
    suppose pragmatically you can leave static types out, and see if the
    compiler complains, then add them as needed.

    Now when I try to help it along, it balks:

    EnumSet<Species> species1 = EnumSet<Species>.of( ASPEN, CEDAR );

    I have never had it complain before about the nugatory help figuring
    out the static type. I am trying to figure out what is special about
    Enumset.of.
     
    Roedy Green, Aug 18, 2005
    #4
  5. Roedy Green

    Roedy Green Guest

    On Thu, 18 Aug 2005 05:59:59 GMT, Roedy Green
    <> wrote or quoted :

    >EnumSet<Breed> breeds = EnumSet<Breed>.of( DALMATIAN, DACHSHUND );


    Mystery solved:

    That should read

    EnumSet<Breed> breeds = EnumSet.<Breed> of( DALMATIAN, DACHSHUND );

    Then the compiler is happy.

    I just had the . in the wrong place.
     
    Roedy Green, Aug 18, 2005
    #5
  6. Roedy Green <> writes:

    > On Thu, 18 Aug 2005 07:26:56 +0100, Thomas Hawtin
    > <> wrote or quoted :


    >>class <E> {
    >> private static List<E> emptyList = new EmptyList<E>();
    >>}

    >
    > I don't think that is the problem is this case. I quote Sun's code
    > from EnumSet:
    >
    > public static <E extends Enum<E>> EnumSet<E> of(E e1, E e2) {

    .....
    > }
    >
    > Granted they do use E as the class type as well, but surely that E is
    > not the same E as on the static, right?


    Correct. The E's seen in the static method are all in the scope of the
    first E (declared in <E extends ...>), and that is the type variable
    they refer to.

    > Then I try to call that static method:
    >
    > The compiler is happy with:

    ....
    > EnumSet species1 = EnumSet.of( ASPEN, CEDAR );
    > which in my opinion is a bug since species1 is then typeless and will
    > let any EnumSet in.


    The unparameterized type "EnumSet" is also called a "raw type". It is
    a backwards compatability hack, and it indeed loses typing
    information. You should not use it as a type in new code.

    It does allows 1.4 code to compile and have a variable that accepts
    all EnumSet's, just as it used to do. Even worse, it allows unsafe
    operations:
    EnumSet species1 = EnumSet.of(ASPEN, CEDAR);
    species.add(LAMBOURGHINI);


    > Now when I try to help it along, it balks:
    >
    > EnumSet<Species> species1 = EnumSet<Species>.of( ASPEN, CEDAR );


    As Thomas Hawtin said, the problem is that the "EnumSet<Species>" type
    does not have a static method. It's the EnumSet *class* that does. So
    if you write

    EnumSet<Species> species1 = EnumSet.of( ASPEN, CEDAR );

    it works because Java can infer the type parameter of the "of"
    method. If you want to explicitly give the value of the type parameter
    of "of", you write it before the method name, as always when calling
    a generic method, i.e. :

    EnumSet<Species> species1 = EnumSet.<Species>of( ASPEN, CEDAR );

    That is, you call the static method "of" on the class "EnumSet",
    and pass it a type parameter of "Species" (as well as two actual
    arguments of that type).

    > I have never had it complain before about the nugatory help figuring
    > out the static type. I am trying to figure out what is special about
    > Enumset.of.


    There is nothing special about EnumSet.of. It works like any other
    generic static method (It's just that there aren't that many those :)

    /L
    --
    Lasse Reichstein Nielsen -
    DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
    'Faith without judgement merely degrades the spirit divine.'
     
    Lasse Reichstein Nielsen, Aug 18, 2005
    #6
  7. Roedy Green

    Tim Tyler Guest

    Lasse Reichstein Nielsen <> wrote or quoted:

    > If you want to explicitly give the value of the type parameter
    > of "of", you write it before the method name, as always when calling
    > a generic method, i.e. :
    >
    > EnumSet<Species> species1 = EnumSet.<Species>of( ASPEN, CEDAR );
    >
    > That is, you call the static method "of" on the class "EnumSet",
    > and pass it a type parameter of "Species" (as well as two actual
    > arguments of that type). [...]


    Ouch.
    --
    __________
    |im |yler http://timtyler.org/ Remove lock to reply.
     
    Tim Tyler, Aug 18, 2005
    #7
  8. Roedy Green

    Roedy Green Guest

    On Thu, 18 Aug 2005 18:58:20 +0200, Lasse Reichstein Nielsen
    <> wrote or quoted :

    >The unparameterized type "EnumSet" is also called a "raw type". It is
    >a backwards compatability hack, and it indeed loses typing
    >information. You should not use it as a type in new code.


    Fine. I can see permitting it, but not letting it by without a warning
    message. All other untyped stuff gets a warning message.

    If it will let that by without a warning it will let by mixed type
    stuff without warning defeating the whole point of this monstrous
    exercise.
     
    Roedy Green, Aug 18, 2005
    #8
  9. Roedy Green

    Roedy Green Guest

    On Thu, 18 Aug 2005 18:58:20 +0200, Lasse Reichstein Nielsen
    <> wrote or quoted :

    >It does allows 1.4 code to compile and have a variable that accepts
    >all EnumSet's, just as it used to do. Even worse, it allows unsafe
    >operations:


    That is nuts since EnumSet was not introduced until 1.5. It has
    absolutely no excuse for being untyped. There is no legacy code.
     
    Roedy Green, Aug 18, 2005
    #9
  10. Roedy Green <> writes:

    > That is nuts since EnumSet was not introduced until 1.5. It has
    > absolutely no excuse for being untyped. There is no legacy code.


    The raw type hack works for all generic classes, so it also works for
    those introduced in 1.5, whether they need it or not.

    Avoinding this would require a way to turn off raw types, which would
    be introducing extra complexity.

    As it is defined, you can assign any EnumSet to the raw EnumSet type,
    but assigning from the raw type to a parameterized type will give a
    warning.

    /L
    --
    Lasse Reichstein Nielsen -
    DHTML Death Colors: <URL:http://www.infimum.dk/HTML/rasterTriangleDOM.html>
    'Faith without judgement merely degrades the spirit divine.'
     
    Lasse Reichstein Nielsen, Aug 18, 2005
    #10
  11. Roedy Green

    Roedy Green Guest

    On Fri, 19 Aug 2005 00:17:35 +0200, Lasse Reichstein Nielsen
    <> wrote or quoted :

    >As it is defined, you can assign any EnumSet to the raw EnumSet type,
    >but assigning from the raw type to a parameterized type will give a
    >warning.


    Why is EnumSet different? If I have an untyped ArrayList the compiler
    issues a warning.
     
    Roedy Green, Aug 19, 2005
    #11
  12. Roedy Green wrote:
    >
    > Why is EnumSet different? If I have an untyped ArrayList the compiler
    > issues a warning.


    It isn't. However contructors are different from methods (I know not why).

    As in my first reply, if you write a method an equivalent of EnumSet.of
    for ArrayList, it'll work exactly the same. Using the other array list:

    List<Breed> breeds = java.util.Arrays.asList(DALMATION, DACHSHUND);

    Tom Hawtin
    --
    Unemployed English Java programmer
    http://jroller.com/page/tackline/
     
    Thomas Hawtin, Aug 22, 2005
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Roedy Green

    EnumSet, what the ?

    Roedy Green, Jul 8, 2005, in forum: Java
    Replies:
    6
    Views:
    1,940
    George Cherry
    Jul 9, 2005
  2. George Cherry

    EnumSet--what the...?

    George Cherry, Jul 9, 2005, in forum: Java
    Replies:
    0
    Views:
    2,396
    George Cherry
    Jul 9, 2005
  3. Ulrich Scholz

    EnumSet + contains: strange behavior

    Ulrich Scholz, May 31, 2006, in forum: Java
    Replies:
    10
    Views:
    1,685
    Christian Kaufhold
    Jun 4, 2006
  4. Eric Smith
    Replies:
    19
    Views:
    2,474
    Eric Smith
    May 17, 2007
  5. Roedy Green

    Enumset

    Roedy Green, Aug 31, 2007, in forum: Java
    Replies:
    10
    Views:
    1,352
    Lasse Reichstein Nielsen
    Sep 1, 2007
Loading...

Share This Page