enums: What the H? again

G

George Cherry

In the little program below I can use
Size.class
for the first parameter of valueOf() or
Size.Large.getDeclaringClass()
but not
Size.Large.getClass().

Why? The compiler's message is very obscure (to me).
I think if I understood this problem I would understand
something I clearly don't--but should.

George Cherry

//Size.Large.getDeclaringClass()
//Size.Large.getClass()

enum Size {
Small(28),
Medium(34),
Large(42);

private int inches;

int getInches() { return inches; }

Size(int inches) {
this.inches = inches;
}
}

public class SizeTest {
public static void main(String args[]) {
Size s = Size.valueOf( Size.class, args[0] );
System.out.println( s + ": " + s.getInches() + " inches." );
System.out.println(
"The type of " + s + " is " + s.getClass().getName()
);
}
}
 
R

Roedy Green

Size s = Size.valueOf( Size.class, args[0] );

You don't need that Size.class. You are using
the internal method. Javac automatically generates you a method with
this signature

Size s = Size.valueOf( args[0] );

I puzzled over this, since I saw people using such a method but could
find no sign of it in the Enum class until I did a decompile and saw
it sitting there automatically generated.

Everything comes clear when you decompile an enum, and see the
generated methods, and the inner classes for each enum constant.

Most of the quirkiness of enum comes from the quirkiness of anonymous
inner classes.

here is a simple enum that involves no inner classes.

package com.mindprod.htmlmacros;

import java.util.EnumSet;

/**
* DO NOT SORT. THE ENUM ORDER IS SIGNIFICANT!! enum of possible
Windows OSes.
* May be used freely for any purpose but military.
*
* @author Roedy Green copyright 2005 Canadian Mind Products
*/
public enum OS {
/**
* Windows 95
*/
WIN95("W95", "Windows 95"),
/**
* Windows 98
*/
WIN98("W98", "Windows 98"),
/**
* Windows Me
*/
WINME("Me", "Windows Me"),
/**
* Windows NT
*/
WINNT("NT", "Windows NT"),
/**
* Windows 2000
*/
WIN2K("W2K", "Windows 2000"),
/**
* Windows XP
*/
WINXP("WXP", "Windows XP"),
/**
* Windows 2003
*/
WIN2K3("W2K3", "Windows 2003"),
/**
* Linux
*/
LINUX("Linux", "Linux"),
/**
* Mac OS/0
*/
CLASSICMAC("OS9", "classic MacIntosh OS/9"),
/**
* Mac OS X
*/
MAC("OSX", "MacIntosh OS X");

private String shortName;

private String longName;

private static boolean DEBUGGING = true;

/**
* Enum constant constructor that captures two extra facts about
the enum.
*
* @param shortName
* name for the os e.g. Me
* @param longName
* name of the OS e.g. "Windows XP"
*/
OS( String shortName, String longName )
{
this.shortName = shortName;
this.longName = longName;
}

/**
* @return short name
*/
public String getShortName ()
{
return this.shortName;
}

/**
* @return long name
*/
public String getLongName ()
{
return this.longName;
}

/**
* Static method to construct a string mentioning multiple OSes,
by slashes.
*
* @param choices
* EnumSet of just the oses you want included
* @return a String of the form "Windows 95/98/Me"
*/
public static String slashList ( EnumSet<OS> choices )
{
StringBuilder sb = new StringBuilder( 40 );
for ( OS o : choices )
{
sb.append( '/' );
sb.append( o.shortName );
}
if ( sb.length() == 0 )
{
return "";
}
else
{
// chop lead /
return sb.toString().substring( 1 );
}
}

/**
* test harness
*
* @param args
* not used
*/
public static void main ( String [ ] args )
{
if ( DEBUGGING )
{
// You don't use a constructor to create EnumSet objects.
EnumSet<OS> justThese = EnumSet.of( WIN2K, WINXP, WINME );

// prints "Me/W2K/XP"
// note they come out in proper order.
System.out.println( OS.slashList( justThese ) );
}
}
}

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

decompilation.
note generated valueOf, values, static finals for each enum constant,
$VALUES array for indexing the constants, two hidden parms to each
enum constant constructor, its name and ordinal.

package com.mindprod.htmlmacros;

import java.io.PrintStream;
import java.util.EnumSet;
import java.util.Iterator;

public final class OS extends Enum
{

public static final OS[] values()
{
return (OS[])$VALUES.clone();
}

public static OS valueOf(String s)
{
return (OS)Enum.valueOf(com/mindprod/htmlmacros/OS, s);
}

private OS(String s, int i, String s1, String s2)
{
super(s, i);
shortName = s1;
longName = s2;
}

public String getShortName()
{
return shortName;
}

public String getLongName()
{
return longName;
}

public static String slashList(EnumSet enumset)
{
StringBuilder stringbuilder = new StringBuilder(40);
OS os;
for(Iterator iterator = enumset.iterator();
iterator.hasNext(); stringbuilder.append(os.shortName))
{
os = (OS)iterator.next();
stringbuilder.append('/');
}

if(stringbuilder.length() == 0)
return "";
else
return stringbuilder.toString().substring(1);
}

public static void main(String args[])
{
if(DEBUGGING)
{
EnumSet enumset = EnumSet.of(WIN2K, WINXP, WINME);
System.out.println(slashList(enumset));
}
}

public static final OS WIN95;
public static final OS WIN98;
public static final OS WINME;
public static final OS WINNT;
public static final OS WIN2K;
public static final OS WINXP;
public static final OS WIN2K3;
public static final OS LINUX;
public static final OS CLASSICMAC;
public static final OS MAC;
private String shortName;
private String longName;
private static boolean DEBUGGING = true;
private static final OS $VALUES[];

static
{
WIN95 = new OS("WIN95", 0, "W95", "Windows 95");
WIN98 = new OS("WIN98", 1, "W98", "Windows 98");
WINME = new OS("WINME", 2, "Me", "Windows Me");
WINNT = new OS("WINNT", 3, "NT", "Windows NT");
WIN2K = new OS("WIN2K", 4, "W2K", "Windows 2000");
WINXP = new OS("WINXP", 5, "WXP", "Windows XP");
WIN2K3 = new OS("WIN2K3", 6, "W2K3", "Windows 2003");
LINUX = new OS("LINUX", 7, "Linux", "Linux");
CLASSICMAC = new OS("CLASSICMAC", 8, "OS9", "classic MacIntosh
OS/9");
MAC = new OS("MAC", 9, "OSX", "MacIntosh OS X");
$VALUES = (new OS[] {
WIN95, WIN98, WINME, WINNT, WIN2K, WINXP, WIN2K3, LINUX,
CLASSICMAC, MAC
});
}
}

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

Here is a more complicated enum with inner classes. Because this enum
relies on static variables you can't have two of these beasts working
independently. On my todo list is writing an essay on enums that will
have to tackle how you would rewrite this avoiding that problem. One
step at a time.

Each enum constant implements the abstract how method a different way:

package com.mindprod.stomp;

/**
* @author Roedy Green
*
* track current state of CD directory so we don't emit needless CD
commands.
*/
public enum InDir {
/** we are in the project directory */
INPROJECT() {

String how ()
{
return "c:\ncd " + projectDir + "\n";
}
},
/** we are in the root */
INROOT() {

String how ()
{
return "c:\ncd \\\n";
}
},

/** we don't know (or care) which directory we are in */
UNKNOWN() {

String how ()
{
return "";
}
};
/**
* which directory we are in now
*/
private static InDir currentState = UNKNOWN;

/**
* the fully qualified project directory. It cannot be private or
* enum constants can't see it.
*/
static String projectDir;

/**
* how you get to this directory
*
* @return possibly empty string of commands to get
* to this directory
*/
abstract String how ();

/**
* no arg constructor
*/
private InDir( )
{
}

/**
* generate code to go to the given directory.
* If we are already there, generates an empty string.
*
* @param wanted which directory we want go to next
* @return commands to take us to that directory.
*/
public static String _cd ( InDir wanted )
{
if ( wanted == currentState )
{
return "";
}
currentState = wanted;
return wanted.how();
}

/**
* @param projectDir
* fully qualified project directory
*/
public static void setProject ( String projectDir )
{
InDir.projectDir = projectDir;
}
}

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

Here is the decompilation. Note how there are three places you can
store information:
1. in the statics for the entire enum
2. in the instance variables used by all enum constants, each with
their own copy.
3. in the anonymous inner class instance variables, specific to each
enum constant.

Note that because enum constants are anonymous inner classes, they are
not allowed to have their own statics.



package com.mindprod.stomp;


public abstract class InDir extends Enum
{

public static final InDir[] values()
{
return (InDir[])$VALUES.clone();
}

public static InDir valueOf(String s)
{
return (InDir)Enum.valueOf(com/mindprod/stomp/InDir, s);
}

abstract String how();

private InDir(String s, int i)
{
super(s, i);
}

public static String _cd(InDir indir)
{
if(indir == currentState)
{
return "";
} else
{
currentState = indir;
return indir.how();
}
}

public static void setProject(String s)
{
projectDir = s;
}


public static final InDir INPROJECT;
public static final InDir INROOT;
public static final InDir UNKNOWN;
private static InDir currentState;
static String projectDir;
private static final InDir $VALUES[];

static
{
INPROJECT = new InDir("INPROJECT", 0) {

String how()
{
return (new StringBuilder()).append("c:\ncd
").append(projectDir).append("\n").toString();
}

};
INROOT = new InDir("INROOT", 1) {

String how()
{
return "c:\ncd \\\n";
}

};
UNKNOWN = new InDir("UNKNOWN", 2) {

String how()
{
return "";
}

};
$VALUES = (new InDir[] {
INPROJECT, INROOT, UNKNOWN
});
currentState = UNKNOWN;
}
}
----------------------------------------------------------

one of the anonymous inner classes looks like this:


package com.mindprod.stomp;


// Referenced classes of package com.mindprod.stomp:
// InDir

static final class InDir$1 extends InDir
{

String how()
{
return (new StringBuilder()).append("c:\ncd
").append(projectDir).append("\n").toString();
}

InDir$1(String s, int i)
{
super(s, i, null);
}
}
--
Bush crime family lost/embezzled $3 trillion from Pentagon.
Complicit Bush-friendly media keeps mum. Rumsfeld confesses on video.
http://www.infowars.com/articles/us/mckinney_grills_rumsfeld.htm

Canadian Mind Products, Roedy Green.
See http://mindprod.com/iraq.html photos of Bush's war crimes
 
G

George Cherry

Roedy Green said:
Size s = Size.valueOf( Size.class, args[0] );

You don't need that Size.class. You are using
the internal method. Javac automatically generates you a method with
this signature

Size s = Size.valueOf( args[0] );

Thanks, Roedy. It does not seem very programmer
friendly to generate useful methods automatically
and not document their existence in either the Sun
tutorials or the docs.
I puzzled over this, since I saw people using such a method but could
find no sign of it in the Enum class until I did a decompile and saw
it sitting there automatically generated.

Yes, it's automatically generated like the frequently
mentioned values() method which returns an array
of the type's constants. Does this mean I have to
decompile code to find out what really exists? I
think that sucks.

GWC
Everything comes clear when you decompile an enum, and see the
generated methods, and the inner classes for each enum constant.

Most of the quirkiness of enum comes from the quirkiness of anonymous
inner classes.

here is a simple enum that involves no inner classes.

package com.mindprod.htmlmacros;

import java.util.EnumSet;

/**
* DO NOT SORT. THE ENUM ORDER IS SIGNIFICANT!! enum of possible
Windows OSes.
* May be used freely for any purpose but military.
*
* @author Roedy Green copyright 2005 Canadian Mind Products
*/
public enum OS {
/**
* Windows 95
*/
WIN95("W95", "Windows 95"),
/**
* Windows 98
*/
WIN98("W98", "Windows 98"),
/**
* Windows Me
*/
WINME("Me", "Windows Me"),
/**
* Windows NT
*/
WINNT("NT", "Windows NT"),
/**
* Windows 2000
*/
WIN2K("W2K", "Windows 2000"),
/**
* Windows XP
*/
WINXP("WXP", "Windows XP"),
/**
* Windows 2003
*/
WIN2K3("W2K3", "Windows 2003"),
/**
* Linux
*/
LINUX("Linux", "Linux"),
/**
* Mac OS/0
*/
CLASSICMAC("OS9", "classic MacIntosh OS/9"),
/**
* Mac OS X
*/
MAC("OSX", "MacIntosh OS X");

private String shortName;

private String longName;

private static boolean DEBUGGING = true;

/**
* Enum constant constructor that captures two extra facts about
the enum.
*
* @param shortName
* name for the os e.g. Me
* @param longName
* name of the OS e.g. "Windows XP"
*/
OS( String shortName, String longName )
{
this.shortName = shortName;
this.longName = longName;
}

/**
* @return short name
*/
public String getShortName ()
{
return this.shortName;
}

/**
* @return long name
*/
public String getLongName ()
{
return this.longName;
}

/**
* Static method to construct a string mentioning multiple OSes,
by slashes.
*
* @param choices
* EnumSet of just the oses you want included
* @return a String of the form "Windows 95/98/Me"
*/
public static String slashList ( EnumSet<OS> choices )
{
StringBuilder sb = new StringBuilder( 40 );
for ( OS o : choices )
{
sb.append( '/' );
sb.append( o.shortName );
}
if ( sb.length() == 0 )
{
return "";
}
else
{
// chop lead /
return sb.toString().substring( 1 );
}
}

/**
* test harness
*
* @param args
* not used
*/
public static void main ( String [ ] args )
{
if ( DEBUGGING )
{
// You don't use a constructor to create EnumSet objects.
EnumSet<OS> justThese = EnumSet.of( WIN2K, WINXP, WINME );

// prints "Me/W2K/XP"
// note they come out in proper order.
System.out.println( OS.slashList( justThese ) );
}
}
}

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

decompilation.
note generated valueOf, values, static finals for each enum constant,
$VALUES array for indexing the constants, two hidden parms to each
enum constant constructor, its name and ordinal.

package com.mindprod.htmlmacros;

import java.io.PrintStream;
import java.util.EnumSet;
import java.util.Iterator;

public final class OS extends Enum
{

public static final OS[] values()
{
return (OS[])$VALUES.clone();
}

public static OS valueOf(String s)
{
return (OS)Enum.valueOf(com/mindprod/htmlmacros/OS, s);
}

private OS(String s, int i, String s1, String s2)
{
super(s, i);
shortName = s1;
longName = s2;
}

public String getShortName()
{
return shortName;
}

public String getLongName()
{
return longName;
}

public static String slashList(EnumSet enumset)
{
StringBuilder stringbuilder = new StringBuilder(40);
OS os;
for(Iterator iterator = enumset.iterator();
iterator.hasNext(); stringbuilder.append(os.shortName))
{
os = (OS)iterator.next();
stringbuilder.append('/');
}

if(stringbuilder.length() == 0)
return "";
else
return stringbuilder.toString().substring(1);
}

public static void main(String args[])
{
if(DEBUGGING)
{
EnumSet enumset = EnumSet.of(WIN2K, WINXP, WINME);
System.out.println(slashList(enumset));
}
}

public static final OS WIN95;
public static final OS WIN98;
public static final OS WINME;
public static final OS WINNT;
public static final OS WIN2K;
public static final OS WINXP;
public static final OS WIN2K3;
public static final OS LINUX;
public static final OS CLASSICMAC;
public static final OS MAC;
private String shortName;
private String longName;
private static boolean DEBUGGING = true;
private static final OS $VALUES[];

static
{
WIN95 = new OS("WIN95", 0, "W95", "Windows 95");
WIN98 = new OS("WIN98", 1, "W98", "Windows 98");
WINME = new OS("WINME", 2, "Me", "Windows Me");
WINNT = new OS("WINNT", 3, "NT", "Windows NT");
WIN2K = new OS("WIN2K", 4, "W2K", "Windows 2000");
WINXP = new OS("WINXP", 5, "WXP", "Windows XP");
WIN2K3 = new OS("WIN2K3", 6, "W2K3", "Windows 2003");
LINUX = new OS("LINUX", 7, "Linux", "Linux");
CLASSICMAC = new OS("CLASSICMAC", 8, "OS9", "classic MacIntosh
OS/9");
MAC = new OS("MAC", 9, "OSX", "MacIntosh OS X");
$VALUES = (new OS[] {
WIN95, WIN98, WINME, WINNT, WIN2K, WINXP, WIN2K3, LINUX,
CLASSICMAC, MAC
});
}
}

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

Here is a more complicated enum with inner classes. Because this enum
relies on static variables you can't have two of these beasts working
independently. On my todo list is writing an essay on enums that will
have to tackle how you would rewrite this avoiding that problem. One
step at a time.

Each enum constant implements the abstract how method a different way:

package com.mindprod.stomp;

/**
* @author Roedy Green
*
* track current state of CD directory so we don't emit needless CD
commands.
*/
public enum InDir {
/** we are in the project directory */
INPROJECT() {

String how ()
{
return "c:\ncd " + projectDir + "\n";
}
},
/** we are in the root */
INROOT() {

String how ()
{
return "c:\ncd \\\n";
}
},

/** we don't know (or care) which directory we are in */
UNKNOWN() {

String how ()
{
return "";
}
};
/**
* which directory we are in now
*/
private static InDir currentState = UNKNOWN;

/**
* the fully qualified project directory. It cannot be private or
* enum constants can't see it.
*/
static String projectDir;

/**
* how you get to this directory
*
* @return possibly empty string of commands to get
* to this directory
*/
abstract String how ();

/**
* no arg constructor
*/
private InDir( )
{
}

/**
* generate code to go to the given directory.
* If we are already there, generates an empty string.
*
* @param wanted which directory we want go to next
* @return commands to take us to that directory.
*/
public static String _cd ( InDir wanted )
{
if ( wanted == currentState )
{
return "";
}
currentState = wanted;
return wanted.how();
}

/**
* @param projectDir
* fully qualified project directory
*/
public static void setProject ( String projectDir )
{
InDir.projectDir = projectDir;
}
}

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

Here is the decompilation. Note how there are three places you can
store information:
1. in the statics for the entire enum
2. in the instance variables used by all enum constants, each with
their own copy.
3. in the anonymous inner class instance variables, specific to each
enum constant.

Note that because enum constants are anonymous inner classes, they are
not allowed to have their own statics.



package com.mindprod.stomp;


public abstract class InDir extends Enum
{

public static final InDir[] values()
{
return (InDir[])$VALUES.clone();
}

public static InDir valueOf(String s)
{
return (InDir)Enum.valueOf(com/mindprod/stomp/InDir, s);
}

abstract String how();

private InDir(String s, int i)
{
super(s, i);
}

public static String _cd(InDir indir)
{
if(indir == currentState)
{
return "";
} else
{
currentState = indir;
return indir.how();
}
}

public static void setProject(String s)
{
projectDir = s;
}


public static final InDir INPROJECT;
public static final InDir INROOT;
public static final InDir UNKNOWN;
private static InDir currentState;
static String projectDir;
private static final InDir $VALUES[];

static
{
INPROJECT = new InDir("INPROJECT", 0) {

String how()
{
return (new StringBuilder()).append("c:\ncd
").append(projectDir).append("\n").toString();
}

};
INROOT = new InDir("INROOT", 1) {

String how()
{
return "c:\ncd \\\n";
}

};
UNKNOWN = new InDir("UNKNOWN", 2) {

String how()
{
return "";
}

};
$VALUES = (new InDir[] {
INPROJECT, INROOT, UNKNOWN
});
currentState = UNKNOWN;
}
}
----------------------------------------------------------

one of the anonymous inner classes looks like this:


package com.mindprod.stomp;


// Referenced classes of package com.mindprod.stomp:
// InDir

static final class InDir$1 extends InDir
{

String how()
{
return (new StringBuilder()).append("c:\ncd
").append(projectDir).append("\n").toString();
}

InDir$1(String s, int i)
{
super(s, i, null);
}
}
--
Bush crime family lost/embezzled $3 trillion from Pentagon.
Complicit Bush-friendly media keeps mum. Rumsfeld confesses on video.
http://www.infowars.com/articles/us/mckinney_grills_rumsfeld.htm

Canadian Mind Products, Roedy Green.
See http://mindprod.com/iraq.html photos of Bush's war crimes
 
C

Chris Uppal

George said:
In the little program below I can use
Size.class
for the first parameter of valueOf() or
Size.Large.getDeclaringClass()
but not
Size.Large.getClass().

Why? The compiler's message is very obscure (to me).
I think if I understood this problem I would understand
something I clearly don't--but should.

The problem, I think, is that if you use the getClass() form, then the compiler
cannot prove to itself that the answered instance of java.lang.Class is a
subclass of Size. More specifically, java.lang.enum.valueOf() hase the
declaration:

public static <T extends Enum<T>> T
valueOf(
Class<T> enumType,
String name)

But the declaration of getClass() is only that it returns:

Class<? extends Object>

So the compiler doesn't "know" that the type of the Class<X> object returned by
getClass() is compatible with more specific requirement for the Class<X extends
enum> required by valueOf().

Arguably, the compiler could special-case getClass() so that it uses it's own
knowledge of the class of the target object to "fill in" the type of the
generic class it returns. Personally, I think that would probably be a good
idea (all this stuff is just the compiler playing with itself anyway, so I
don't see why it shouldn't do it properly). But it doesn't seem to have the
smarts...

BTW, there's another reason why getClass() might not be the right form to use,
which is to do with how the implementations of enums works. First off (just
for interest, as much as anything) here's a complete decompilation of your
example Size class (done by JAD then cleaned up by hand):

===============
final class Size
extends Enum
{
public static final Size Small;
public static final Size Medium;
public static final Size Large;
private static final Size VALUES[];
private int inches;

static
{
Small = new Size("Small", 0, 28);
Medium = new Size("Medium", 1, 34);
Large = new Size("Large", 2, 42);
VALUES = new Size[]
{
Small,
Medium,
Large
};
}

public static final Size[]
values()
{
return (Size[])VALUES.clone();
}

public static Size
valueOf(String name)
{
for (Size size : VALUES)
if (size.name().equals(name))
return size;
throw new IllegalArgumentException(name);
}

private
Size(String s, int i, int inches)
{
super(s, i);
this.inches = inches;
}

int
getInches()
{
return inches;
}
}
===============

The problem comes when you use instance-specific methods in your enum. E.g. if
the definition were:

===============
enum Size
{
Small(28) { String goldilocks() { return "too small"; } },
Medium(34) { String goldilocks() { return "just right"; } },
Large(42) { String goldilocks() { return "too big"; } };

private int inches;

int getInches() { return inches; }
abstract String goldilocks();
Size(int inches) { this.inches = inches; }
}

public class
SizeTest
{
public static void
main(String args[])
{
Size s = Size.valueOf(
// (Class<Size>)(Size.Medium.getClass()),
Size.class,
args[0]);
System.out.print(s + ": " + s.getInches() + " inches");
System.out.println(", which is " + s.goldilocks());
System.out.println("The type of " + s + " is " +
s.getClass().getName());
}
}
===============

then each of Small, Medium, and Large would be of different synthetic
classes -- each a subclass of Size. The problem comes because if you use
Medium's Class object (with a cast to dodge the complaints from the compiler,
commented out above) then it will fail at runtime. The class object for Size
has been initialised with the information used by valueOf(), but the class
object for Medium has not.

A bit academic, perhaps, but I hope moderately interesting anyway...

-- chris
 
S

Stefan Schulz

Is there some Javadoc tag you can use to thwart IDE method reordering
when it is fragile?

Use an IDE which does not assume to be smarter then you (eclipse, for
example)
 
G

George Cherry

Wow! Thanks for your generous response below.
I'm sure that it will clarify this issue for me.

George
The problem, I think, is that if you use the getClass() form, then the
compiler
cannot prove to itself that the answered instance of java.lang.Class is a
subclass of Size. More specifically, java.lang.enum.valueOf() hase the
declaration:

public static <T extends Enum<T>> T
valueOf(
Class<T> enumType,
String name)

But the declaration of getClass() is only that it returns:

Class<? extends Object>

So the compiler doesn't "know" that the type of the Class<X> object
returned by
getClass() is compatible with more specific requirement for the Class<X
extends
enum> required by valueOf().

Arguably, the compiler could special-case getClass() so that it uses it's
own
knowledge of the class of the target object to "fill in" the type of the
generic class it returns. Personally, I think that would probably be a
good
idea (all this stuff is just the compiler playing with itself anyway, so I
don't see why it shouldn't do it properly). But it doesn't seem to have
the
smarts...

BTW, there's another reason why getClass() might not be the right form to
use,
which is to do with how the implementations of enums works. First off
(just
for interest, as much as anything) here's a complete decompilation of your
example Size class (done by JAD then cleaned up by hand):

===============
final class Size
extends Enum
{
public static final Size Small;
public static final Size Medium;
public static final Size Large;
private static final Size VALUES[];
private int inches;

static
{
Small = new Size("Small", 0, 28);
Medium = new Size("Medium", 1, 34);
Large = new Size("Large", 2, 42);
VALUES = new Size[]
{
Small,
Medium,
Large
};
}

public static final Size[]
values()
{
return (Size[])VALUES.clone();
}

public static Size
valueOf(String name)
{
for (Size size : VALUES)
if (size.name().equals(name))
return size;
throw new IllegalArgumentException(name);
}

private
Size(String s, int i, int inches)
{
super(s, i);
this.inches = inches;
}

int
getInches()
{
return inches;
}
}
===============

The problem comes when you use instance-specific methods in your enum.
E.g. if
the definition were:

===============
enum Size
{
Small(28) { String goldilocks() { return "too small"; } },
Medium(34) { String goldilocks() { return "just right"; } },
Large(42) { String goldilocks() { return "too big"; } };

private int inches;

int getInches() { return inches; }
abstract String goldilocks();
Size(int inches) { this.inches = inches; }
}

public class
SizeTest
{
public static void
main(String args[])
{
Size s = Size.valueOf(
// (Class<Size>)(Size.Medium.getClass()),
Size.class,
args[0]);
System.out.print(s + ": " + s.getInches() + " inches");
System.out.println(", which is " + s.goldilocks());
System.out.println("The type of " + s + " is " +
s.getClass().getName());
}
}
===============

then each of Small, Medium, and Large would be of different synthetic
classes -- each a subclass of Size. The problem comes because if you use
Medium's Class object (with a cast to dodge the complaints from the
compiler,
commented out above) then it will fail at runtime. The class object for
Size
has been initialised with the information used by valueOf(), but the class
object for Medium has not.

A bit academic, perhaps, but I hope moderately interesting anyway...

-- chris
 

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
474,432
Messages
2,571,681
Members
48,796
Latest member
Greg L.

Latest Threads

Top