EnumSet Generics puzzle

R

Roedy Green

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?
 
T

Thomas Hawtin

Roedy said:
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 said:
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
 
R

Roedy Green

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?
 
R

Roedy Green

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

Roedy Green

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

Lasse Reichstein Nielsen

Roedy Green said:
On Thu, 18 Aug 2005 07:26:56 +0100, Thomas Hawtin


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

Tim Tyler

Lasse Reichstein Nielsen said:
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.
 
R

Roedy Green

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

Roedy Green

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

Lasse Reichstein Nielsen

Roedy Green said:
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
 
R

Roedy Green

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

Thomas Hawtin

Roedy said:
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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,767
Messages
2,569,572
Members
45,045
Latest member
DRCM

Latest Threads

Top