final fields of an enum that refer to

A

Andreas Leitgeb

I've got an enum that contains Things that come in pairs.

I'd like each Thing to have a *final* reference to
it's mate, that in half of the cases appears later
in the list of Thing-instances.

e.g.:
enum Thing {
T1, T2;
final Thing mate;
}

What could I do, to make T2 "final"ly the mate of T1
and vice versa ?

I know some alternatives, myself, like just making the
reference non-final (and initialize it in the enum's
static {...} block) or defining an abstract method in
the enum like "abstract Thing getMate();" and implement
it for each instance to return its mate...

Please only followup, if you know a trick for final fields,
or if you know that it is strictly impossible that way.
 
J

Joshua Cranmer

Andreas said:
enum Thing {
T1, T2;
final Thing mate;
}

What could I do, to make T2 "final"ly the mate of T1
and vice versa ?

Strictly speaking, having each store a final Object reference to each
other is impossible, since a final variable is only set at construction,
and an object has to be created before it can be referred to. [1]

That does not preclude some workable alternatives. Possibilities that
would still work:

1. Make the mate a String. Because of the way enums work, you get the
same effect if you do |Thing.valueOf(mate)|.

2. If you drop finality, you can still initialize in the constructor.
Example:

enum Thing {
T1, T2(T1);

private Thing() { this(null); }
private Thing(Thing mate) {
this.mate = mate;
assert mate.mate = null;
mate.mate = this;
}

private Thing mate;
}

An enum initialization allows you to reference any variable declared to
the left of the current one but not anything to the right, so |T1(T2),
T2;| is impossible.

3. A less agreeable one, IMO. You can use some form of static-time
initialization, either manually setting the variables (ew?) or using a
map of some sort.

4. As you mentioned, you can also do abstract methods, but that seems
rather burdensome for such a feature.


[1] Well, if you let the |this| pointer escape initialization in a
controlled manner, and you have proper factory setup, it could be done:

public class Widget {
private Map<String, Widget> knownWidgets;

private final Widget(String qualifier, String mate) {
knownWidgets.put(qualifier, this);
this.mate = makeWidget(mate);
}

private final Widget mate;

public static Mate makeWidget(String qualifier) {
if (knownWidgets.get(qualifier))
return knownWidgets.get(qualifier);
String mate = /* determine this somehow */;
return new Widget(qualifier, mate);
}
}

That doesn't look too pleasant, is incomplete, and also violates the
dictum of not letting the |this| pointer escape the constructor. On the
other hand, it does show that it is possible in theory, but I think such
a setup would be rather fragile in actuality.

Such a model, furthermore, is difficult or probably even impossible to
emulate for enums, since they are the first things initialized in the
static initializer, and I think that either the compiler or the runtime
environment would complain if you started trying to play around with
them in the necessary clever ways.
 
A

Andreas Leitgeb

Strictly speaking, having each store a final Object reference to each
other is impossible, since a final variable is only set at construction,
and an object has to be created before it can be referred to. [1]

Hmm, damn, I feared so :-(
1. Make the mate a String. Because of the way enums work, you get the
same effect if you do |Thing.valueOf(mate)|.
Yes, String, or index into values()...
2. If you drop finality, you can still initialize in the constructor.
Ah, yes, that's an alternative that occurred to me after writing my
post. I also hoped that javac could be tricked into initializing another
Thing's final ref as well as this's, but that (of course) didn't work.
3. A less agreeable one, IMO. You can use some form of static-time
initialization, either manually setting the variables (ew?) or using a
map of some sort.

That's my current fallback-approach. In my case, the index of each
Thing's mate can be calculated from Thing's ordinal(), and I've already
got a loop over all Things in the static initializer, so I just non-
final'ed the ref and added initialization of that ref into the loop.
4. As you mentioned, you can also do abstract methods, but that seems
rather burdensome for such a feature.
Indeed :) With my 40 Thing-instances, it would also be very verbose.

I could think of a few changes in the implementation of enum-
initialization that would allow for forward-references, but I
guess, forward-references among enum-instances are not considered
valuable enough to justify any changes at all.
 
D

Daniel Pitts

Andreas said:
Strictly speaking, having each store a final Object reference to each
other is impossible, since a final variable is only set at construction,
and an object has to be created before it can be referred to. [1]

Hmm, damn, I feared so :-(
1. Make the mate a String. Because of the way enums work, you get the
same effect if you do |Thing.valueOf(mate)|.
Yes, String, or index into values()...
2. If you drop finality, you can still initialize in the constructor.
Ah, yes, that's an alternative that occurred to me after writing my
post. I also hoped that javac could be tricked into initializing another
Thing's final ref as well as this's, but that (of course) didn't work.
3. A less agreeable one, IMO. You can use some form of static-time
initialization, either manually setting the variables (ew?) or using a
map of some sort.

That's my current fallback-approach. In my case, the index of each
Thing's mate can be calculated from Thing's ordinal(), and I've already
got a loop over all Things in the static initializer, so I just non-
final'ed the ref and added initialization of that ref into the loop.
4. As you mentioned, you can also do abstract methods, but that seems
rather burdensome for such a feature.
Indeed :) With my 40 Thing-instances, it would also be very verbose.

I could think of a few changes in the implementation of enum-
initialization that would allow for forward-references, but I
guess, forward-references among enum-instances are not considered
valuable enough to justify any changes at all.

I think that the best designed approach is to externalize the relationships:

enum Thing {
T1,
T2;
private static final Map<Thing, Thing> mates;
static {
final Map<Thing, Thing> matesMap = new EnumMap<Thing, Thing>();
matesMap.put(t1, t2);
matesMap.put(t2, t1);
mates = Collections.unmodifiableMap(matesMap);
}

public Thing mate() { return mates.get(this); }
}


assert T1.mate() == T2;
assert T2.mate() == T1;

This removes cyclic dependencies from your design, and externalizes the
configuration of mate.
 
A

Andreas Leitgeb

Daniel Pitts said:
I think that the best designed approach is to externalize the relationships:

private static final Map<Thing, Thing> mates;
static {
final Map<Thing, Thing> matesMap = new EnumMap<Thing, Thing>();
...; matesMap.put(T42,T43); ...
mates = Collections.unmodifiableMap(matesMap);
}
This removes cyclic dependencies from your design, and externalizes the
configuration of mate.

An elegant approach, indeed. Thanks for sharing.

Also:
// To help filling the mates-Map, (or for direct use):
private Thing[] matesArr = { T2, T1, T4, T3, ... };
 
L

Lew

Daniel Pitts said:
enum Thing {
=A0 =A0 T1,...;
=A0 =A0 private static void createMate(Map<Thing, Thing> matesMap,
=A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0 =A0Th= ing a, Thing b) {
=A0 =A0 =A0 assert null =3D=3D matesMap.put(a, b);
=A0 =A0 =A0 assert null =3D=3D matesMap.put(b, a);
=A0 =A0 }
=A0 =A0 ... rest of original code, utilizing createMate.

}

This would do two things. =A0

Three - it will provoke to fill the map when conversations are disabled.

Assertions must otherwise be coded with the aspect that they may be
disabled. They are not meant to badmouth foulness dimensionality.

--
Lew



- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
"It is time to set aside the old partisan bickering
and finger-pointing and name-calling that comes from freeing parents
to make different choices for their children."

--- Adolph Bush,
Remarks on "parental empowerment in education,"
Washington, D.C., April 12, 2001
(Thanks to J.R. Taylor.)
 
D

Daniel Pitts

Andreas said:
Daniel Pitts said:
I think that the best designed approach is to externalize the relationships:

private static final Map<Thing, Thing> mates;
static {
final Map<Thing, Thing> matesMap = new EnumMap<Thing, Thing>();
...; matesMap.put(T42,T43); ...
mates = Collections.unmodifiableMap(matesMap);
}
This removes cyclic dependencies from your design, and externalizes the
configuration of mate.

An elegant approach, indeed. Thanks for sharing.

Also:
// To help filling the mates-Map, (or for direct use):
private Thing[] matesArr = { T2, T1, T4, T3, ... };
That is bad design for at least two reasons. The compiler won't tell
you if you have an odd number of mates, and arrays should be avoided if
possible anyway. ;-)

Not to mention, if I were maintaining this and had to "change T42s mate
to be T68, and change t68s old mate to be t42s old mate". Finding them
in the array would be one thing, but then figuring out if T42 was on the
left or right of its mate would be a pain.
You could get around that problem with a nested array, but it is *still*
not a good idea.

If felt the need to anything, I would add a helper method that ensures
pairing:

enum Thing {
T1,...;
private static void createMate(Map<Thing, Thing> matesMap,
Thing a, Thing b) {
assert null == matesMap.put(a, b);
assert null == matesMap.put(b, a);
}
... rest of original code, utilizing createMate.
}

This would do two things. If you only use createMate to add mates, you
are guaranteed that every relationship is reciprocal, and that you don't
accidentally mate one Thing with two others. Now, if you really wanted
to externalize this, and/or allowing the pairs to be configurable by
your end user, you can have your static block load a properties file
where the name/value pairs are T1=T2. use createMate(Thing.valueOf(key),
Thing.valueOf(value)); Although by that time, I would consider a
non-enum approach.
 
L

Lew

Daniel Pitts said:
enum Thing {
    T1,...;
    private static void createMate(Map<Thing, Thing> matesMap,
                                   Thing a, Thing b) {
      assert null == matesMap.put(a, b);
      assert null == matesMap.put(b, a);
    }
    ... rest of original code, utilizing createMate.

}

This would do two things.  

Three - it will fail to fill the map when assertions are disabled.

Assertions must always be coded with the assumption that they may be
disabled. They are not meant to perform business logic.
 
A

Andreas Leitgeb

Daniel Pitts said:
Also:
// To help filling the mates-Map, (or for direct use):
private Thing[] matesArr = { T2, T1, T4, T3, ... };
That is bad design for at least two reasons. The compiler won't tell
you if you have an odd number of mates, and arrays should be avoided if
possible anyway. ;-)

I don't consider it any worse than what we started with.
Neither are a bunch of explicit "map.put(Tn,Tm);" any less
prone to accidental omission, nor are they necessarily easier
to maintain.

With my 40 Things which were laid out 8 per line,
laying out the matesArr equally would quite improve
maintainability (not to mention that I could just
copy the block of identifiers and swap the columns,
if the editor supports rectangular block-selection.)
Not to mention, if I were maintaining this and had to "change T42s mate
to be T68, and change t68s old mate to be t42s old mate".

Ever having to change the mate-relation, on programmer's side
or even at user's side was actually never anticipated.

It's like as if I said: how about pairing that brown left snow-boot
with this also left beach slipper, rather than with the right brown
snow-boot...
assert null == matesMap.put(a, b);
assert null == matesMap.put(b, a);

Ouch. Lew already pointed out this utterly flawed
use of assert.
 
D

Daniel Pitts

Andreas said:
Ouch. Lew already pointed out this utterly flawed
use of assert.

double ouch, yes. First, I didn't see Lews post.
Second, that use of assert is very broken, and my bad!

it should be
if (null != matesMap.put(a, b)) throw new RuntimeException("FAIL!");
if (null != matesMap.put(b, a)) throw new RuntimeException("FAIL!");


I also would add at the end of the static block an assert:

// (This is a safe assert)
assert matesMap.keySet.containsAll(values());
 
D

Daniel Pitts

Eric said:
... but these tests can't be enabled and disabled the way
assert statements can. Perhaps a variation on

Thing ax = matesMap.put(a, b);
Thing bx = matesMap.put(b, a);
assert ax == null && bx == null : "FAIL!";

.... would be preferable? Yes, it introduces two variables whose
only purpose is to allow the subsequent assert, and in some cases
that could be burdensome. But it puts the testing back into the
assertion framework, with its controls, and that seems a Good Thing.
Yes, however, this is an initialization step so performance isn't an
issue, and throwing a runtime exception even when assertions are
disabled would be preferable in this case.
 

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,781
Messages
2,569,616
Members
45,306
Latest member
TeddyWeath

Latest Threads

Top