Using an enum in a constructor

W

Wojtek

Given the following:
---------------------------------
public class Foo
{
private static final int DEFAULT_LENGTH = 30;
private Type ivType;
private int ivLength;

public enum Type
{
OTHER,
FIXED,
VARIABLE;
}

public Foo(Type type)
{
this(type,DEFAULT_LENGTH);
}

public Foo(Type.VARIABLE varType, int length)
{
this(varType,length);
}

private Foo(Type type, int length)
{
super();
ivType = type;
ivLength = length;
}
}
---------------------------------

The compiler complains that Type.VARIABLE cannot be used. Obviously
what I want is that if the Type is VARIABLE, then I want the length in
the constructor, otherwise I will use the default length.

And yes I know I can have a constructor that only takes (int length)
and then assume that the Type is VARIABLE. That is not the point here.
 
G

Gary Coulbourne

Wojtek said:
The compiler complains that Type.VARIABLE cannot be used. Obviously what
I want is that if the Type is VARIABLE, then I want the length in the
constructor, otherwise I will use the default length.

I don't believe you can do what you're asking in Java as it stands. All
of the elements of your enum are of type Type. So Type VARIABLE isn't
special in any way that would allow the constructor to differentiate.
You could, perhaps, make a function that took variable arguments, and if
the first argument was of type VARIABLE, only then look to the second
argument. But, then you'd lose compile-time type safety.

Peace,
Gary
 
D

Daniel Pitts

Given the following:
---------------------------------
public class Foo
{
private static final int DEFAULT_LENGTH = 30;
private Type ivType;
private int ivLength;

public enum Type
{
OTHER,
FIXED,
VARIABLE;
}

public Foo(Type type)
{
this(type,DEFAULT_LENGTH);
}

public Foo(Type.VARIABLE varType, int length)
{
this(varType,length);
}

private Foo(Type type, int length)
{
super();
ivType = type;
ivLength = length;
}}

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

The compiler complains that Type.VARIABLE cannot be used. Obviously
what I want is that if the Type is VARIABLE, then I want the length in
the constructor, otherwise I will use the default length.

And yes I know I can have a constructor that only takes (int length)
and then assume that the Type is VARIABLE. That is not the point here.


Try using the Static Factory approach instead

public class Foo {
enum Type {
a, b, c
}

private Foo(Type type, int length) {
// ...
}

public static Foo createVariable(int length) {
return new Foo(Type.a, length);
}

public static Foo createSomething(Type type) {
return new Foo(type, DEFAULT_LENGTH;
}
public sattic Foo createSomething(Type type, int length) {
return new Foo(type, length);
}
}
 
W

Wojtek

Daniel Pitts wrote :
Try using the Static Factory approach instead

public class Foo {
enum Type {
a, b, c
}

private Foo(Type type, int length) {
// ...
}

public static Foo createVariable(int length) {
return new Foo(Type.a, length);
}

public static Foo createSomething(Type type) {
return new Foo(type, DEFAULT_LENGTH;
}
public sattic Foo createSomething(Type type, int length) {
return new Foo(type, length);
}
}

This is the same as having a constructor which takes just (int length).
I still need to make an assumption that type is VARIABLE.
 
W

Wojtek

Gary Coulbourne wrote :
I don't believe you can do what you're asking in Java as it stands.

That is what I thought. It would be nice to be able to enforce this
through type safety though.
All
of the elements of your enum are of type Type. So Type VARIABLE isn't
special in any way that would allow the constructor to differentiate.

Well yes, except that all the enum elements are there. The way I see
it, the enum is the type, but the enum elements are a enum "sub-type",
statically created the first time the class is used. The run-time
should be able to differentiate between them.
 
D

Daniel Pitts

Daniel Pitts wrote :









This is the same as having a constructor which takes just (int length).
I still need to make an assumption that type is VARIABLE.

Its not an assumption, its explicit by the name of the method
"createVariable"!

In general though, if you have a "Type" token, you might be going
about your solution the wrong way. Have you considered using a more
polymorphic approach?

abstract class Foo {
}

class VariableFoo extends Foo {
}

class OtherFoo extends Foo {
}

etc...

That way, the "Type" of foo, is actually the *type* of foo! (Go
figure).

Or, if the Type can change over time, use the State pattern (same
idea, just wrapper)

class Foo {
FooType type;

public Foo(FooType type) {}
}

abstract class FooType {
}

class FooVariable extends FooType {
FooVariable(int length) {}
}

class FooOther extends FooType {
}
 
W

Wojtek

Daniel Pitts wrote :
Its not an assumption, its explicit by the name of the method
"createVariable"!

Sorry, did not read it through :-(
In general though, if you have a "Type" token, you might be going
about your solution the wrong way. Have you considered using a more
polymorphic approach?

abstract class Foo {
}

class VariableFoo extends Foo {
}

class OtherFoo extends Foo {
}

etc...

Yes, it could be done that way. I am refactoring the class and its use
anyways, so 6 == dozen/2, though your six has benefits.

I am converting:

public static final int VARIABLE = 0x01;
public static final int OTHER = 0x02;

etc, and converting to an enum seemed a natural approach

I only have 800+ places to refactor...
 
D

Daniel Pitts

Daniel Pitts wrote :



Sorry, did not read it through :-(






Yes, it could be done that way. I am refactoring the class and its use
anyways, so 6 == dozen/2, though your six has benefits.

I am converting:

public static final int VARIABLE = 0x01;
public static final int OTHER = 0x02;

etc, and converting to an enum seemed a natural approach

I only have 800+ places to refactor...

:)

Moving from "int" style type-codes to "enum" style is a step in the
right direction. Replacing types-codes/switches with polymorphism is
the real goal of an OO designer.

I suggest reading a good refactoring book. I've read both
"Refactoring" by Martin Fowler, and "Refactoring to Patterns" by
Joshua Kerievsky. I would recommend either one (Kerievsky frequently
references Fowler).
 
L

Lasse Reichstein Nielsen

Wojtek said:
Well yes, except that all the enum elements are there. The way I see
it, the enum is the type, but the enum elements are a enum "sub-type",

No, they are values, not types (even though there might be a separate
anonymous class hidden in the implementation).
An enumerated type has a fixed number of values.

What you might want is a constructor:

public Foo(int length) {
this(Type.VARIABLE, length);
}

or if you that's too obscure (it probably is), use a factory method
with a telling name:

public static Foo createVariabelFoo(int length) {
return new Foo(Type.VARIABLE, length);
}

/L
 
R

Roedy Green

public class Foo
{
private static final int DEFAULT_LENGTH = 30;
private Type ivType;
private int ivLength;

public enum Type
{
OTHER,
FIXED,
VARIABLE;
}

You have this as a nested inner instance class. I have always made my
enums separate top level classes. Perhaps you are allowed to define
them as static inner classes. Perhaps there are magic exceptions made
for enums to the usual nesting rules. Is there a language lawyer
about?
 
J

Joshua Cranmer

Roedy said:
You have this as a nested inner instance class. I have always made my
enums separate top level classes. Perhaps you are allowed to define
them as static inner classes. Perhaps there are magic exceptions made
for enums to the usual nesting rules. Is there a language lawyer
about?

JLS §8.9 (I think; I haven't checked since Wednesday) says that all
enums are implicitly static classes and not inner instance classes.
Therefore:

public class Foo
{
public enum Type { }
}

and

public class Foo
{
public static enum Type { }
}

are equivalent.
 
W

Wojtek

Roedy Green wrote :
You have this as a nested inner instance class. I have always made my
enums separate top level classes. Perhaps you are allowed to define
them as static inner classes. Perhaps there are magic exceptions made
for enums to the usual nesting rules. Is there a language lawyer
about?

We have had this discussion before, but I do not remember seeing your
example.

If I use the above, then I reference it with Foo.Type.FIXED.

What does your separate top level enum class look like, and how do you
reference it?
 
S

smcardle

Given the following:
---------------------------------
public class Foo
{
private static final int DEFAULT_LENGTH = 30;
private Type ivType;
private int ivLength;

public enum Type
{
OTHER,
FIXED,
VARIABLE;
}

public Foo(Type type)
{
this(type,DEFAULT_LENGTH);
}

public Foo(Type.VARIABLE varType, int length)
{
this(varType,length);
}

private Foo(Type type, int length)
{
super();
ivType = type;
ivLength = length;
}}

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

The compiler complains that Type.VARIABLE cannot be used. Obviously
what I want is that if the Type is VARIABLE, then I want the length in
the constructor, otherwise I will use the default length.

And yes I know I can have a constructor that only takes (int length)
and then assume that the Type is VARIABLE. That is not the point here.

So what about this then

public class Foo
{
private static final int DEFAULT_LENGTH = 30;
private Type ivType;
private int ivLength;

public enum Type
{
OTHER,
FIXED,
VARIABLE;
}


public Foo(Type type)
{
this(type,DEFAULT_LENGTH);
}


public Foo(Type type, int length)
{
ivType = type;
switch(type) {
case VARIABLE:
ivLength = DEFAULT_LENGTH;
break;
default:
ivLength = length;

}
}
}


Steve
 
L

Lew

switch(type) {
case VARIABLE:
ivLength = DEFAULT_LENGTH;
break;
default:
ivLength = length;

}

Nine times out of ten, a switch() like this is a red flag that polymorphism
should be used instead.
 
D

Daniel Pitts

Roedy Green wrote :





We have had this discussion before, but I do not remember seeing your
example.

If I use the above, then I reference it with Foo.Type.FIXED.

What does your separate top level enum class look like, and how do you
reference it?

/* MySeperateEnum.java */
public enum MySeperateEnum {
A,B,C;
}

/*SomeOtherClass.java*/
public class SomeOtherClass {
MySeperateEnum value = MySeperateEnum.A;

}
 
W

Wojtek

Daniel Pitts wrote :
/* MySeperateEnum.java */
public enum MySeperateEnum {
A,B,C;
}

/*SomeOtherClass.java*/
public class SomeOtherClass {
MySeperateEnum value = MySeperateEnum.A;

}

Ah, ok, I see now. This enum beast is kind of a funny sort of class....

So I can create a separate class for the enum (as per your example), or
nest it in the class where it is used (as per my example).

So if it is being used elsewhere in the system I need two import
statements vs one.

I kind of prefer the nesting approach, as the location (and reference)
indicates the enums logical ownership.
 
D

Daniel Pitts

Sorry if this gets double posted. GG is on the fritz again!
Daniel Pitts wrote :







Ah, ok, I see now. This enum beast is kind of a funny sort of class....
Actually, it is in more ways than you might suspect.

enum MyEnum {
A {
public void doSomething() {
System.out.println("Something on A");
}
},
B{
public void doSomething() {
System.out.println("B does something");
}
},
C{
public void doSomething() {
System.out.println("C you later!");
}
};

public abstract void doSomething();
}

MyEnum is an abstract base class of MyEnum.A (and .B, etc..). You can
add methods, fields (good practice to make those final), and even
constructors to the enum and members. You can even add members to the
specifics (MyEnum.A) that aren't in the parent (MyEnum).


I've used this idiom to implement the flyweight pattern in conjunction
with the strategy/state pattern. Although, I usually find that
eventually I need even more flexibility (better polymorphic
structure), and end up refactoring away from the enum approach. Not
to say enums aren't a good interim step, or prototype step before that
approach.
So I can create a separate class for the enum (as per your example), or
nest it in the class where it is used (as per my example).

So if it is being used elsewhere in the system I need two import
statements vs one.
I don't count imports as statements personally. I think of them more
as directives. You can always give the fully qualified class name in
place of using the imports.
I kind of prefer the nesting approach, as the location (and reference)
indicates the enums logical ownership.
Indeed. If the enum has a logical owner, then it makes much sense to
have that owner, ahem, own it!

On the other hand, if it doesn't have a *single* logical owner (which
does happen on occasion), it should probably be its own separate
entity.


Good luck,
Daniel.
 
L

Lew

Roedy said:
Enums made no sense until I started decompiling code and seeing how
they work under the hood. I have posted the results of my experiments
at http://mindprod.com/jgloss/enum.html

I love that article. It clarifies enum semantics for me.

I do have one small nit over it, with respect to specifics of nested class
terminology:
I have managed to get nested static inner enums and nested instance inner enums to work as well,

There isn't actually a difference for enums:
Nested enum types are implicitly static.
It is permissable to explicitly declare a nested enum type to be static.
<http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.9>

Also, an "inner class" according to the JLS is the opposite of static nested,
which both are subcategories of nested classes:
An /inner class/ is a nested class that is not explicitly or implicitly declared static.
(emphasis original)
<http://java.sun.com/docs/books/jls/third_edition/html/classes.html#8.1.3>

The terms as defined by the JLS are much more specific than they are in
general usage.
 
J

Joshua Cranmer

Roedy said:
Enums made no sense until I started decompiling code and seeing how
they work under the hood. I have posted the results of my experiments
at http://mindprod.com/jgloss/enum.html

In answer to your question:
I wondered wonder why Sun didn't just use the enum ordinals directly
as case numbers.

My current understanding is that it is to promote binary compatibility:
changing the order of the enum constants wouldn't break the switch
statement under this scheme. Sun really does go far to try and keep code
binary-compatible. (I believe Andrew Thompson was the one who pointed
this out sometime over the summer)
 

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

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,822
Latest member
israfaceZa

Latest Threads

Top