Complicate generics in Java - java

I have one supertype defined as:
public abstract class AType<T> {
....
private T value;
private T mask;
public T getValue() {
if (isMasking())
return null;
return this.value;
}
public void setValue(T value) {
if (value == null)
throw new IllegalArgumentException("Value is mandatory.");
this.value = value;
}
protected T getMask() {
if (!isMasking())
return null;
return this.mask;
}
protected void setMask(T mask) {
if (mask == null)
throw new IllegalArgumentException("Mask is mandatory.");
this.setMasking(true);
this.mask = mask;
}
...
}
and few subtypes like:
public class SpecType extends AType<Integer> {
...
}
these sub types specifies the unknown parameter.... i have more f.e. IPv4, Long, and so on
now i need to somehow in runtime do a dynamic cast...
i have these classes defined in enum like this:
public enum Type {
SOME_TYPE(new TypeID(0, (short) 0), OFMU16.class,
new Instantiable<AType<?>>() {
#Override
public SpecType instantiate() {
return new SpecType(new OFMatchTypeIdentifier(0, (short) 0));
}
}),...;
...
public Class<? extends AType<?>> toClass() {
return this.clazz;
}
...
}
I want do something like:
AType<?> type = SOME_TYPE.newInstance(); //this works
SOME_TYPE.toClass().cast(type).setValue(10); //this don't work
so I have to do it statically:
((SpecType) type).setValue(10);
Everything would be OK, but the user of this module will not want to look in enum and cast manually every time. This will probably make mistakes and spend a lot of time with debugging :/....
My question is how can I refactor this or how do I define structure of inheritance to allow user to cast dynamically? Is it possible?
Edit:
I am parsing packets from network. There is a lot types which differs in Vendor Type identifier and type of Value/Mask - these fields are all constant for every this combination, so i has defined it as enum constants. F.e. 20 have different only TypeID but same VendorID and all of them can be represented as Integer, next 10 differ in VendorID And TypeID but all of them can be represented as Short and so on.

It's still not clear why you should have to cast at all. As soon as SOME_TYPE is written into your sourcecode OR the type of set setValue method is hardcoded (in your example int or Integer) you don't need runtime checking - you need compile time checking.
So I suppose the following snippet is how your API users should code:
public class TypeTest {
public static void main(String[] args) {
AType<Integer> type0 = Types.SOME_TYPE_0.instantiate();
type0.setValue(10);
AType<String> type1 = Types.SOME_TYPE_1.instantiate();
type1.setValue("foo");
}
}
I have stripped down your example to the bare minimum which is required to understand the Generics part:
abstract class AType<T> {
private T value;
// standard getter/setter
public T getValue() { return this.value; }
public void setValue(T value) { this.value = value; }
}
class SpecTypeInt extends AType<Integer> {
}
class SpecTypeString extends AType<String> {
}
interface Instantiable<T> {
T instantiate();
}
The key part is: Don't use an enum, because an enum cannot have type parameters. You can use a plain interface instead like the next snippet. Each reference in the interface points to a factory. Each factory knows a) the abstract type and b) the concrete type. To make Generics happy you have to glue a) and b) together with ? extends X.
interface Types {
Instantiable<? extends AType<Integer>> SOME_TYPE_0 = new Instantiable<SpecTypeInt>() {
#Override
public SpecTypeInt instantiate() {
return new SpecTypeInt();
}
};
Instantiable<? extends AType<String>> SOME_TYPE_1 = new Instantiable<SpecTypeString>() {
#Override
public SpecTypeString instantiate() {
return new SpecTypeString();
}
} ;
}
Cleanup: Must your user look into the interface: Yes, he must in any case, because he must know which is the appropriate type for setValue 1. NO solution can circumvent this. Although Eclipse might help you and your users a little bit: In main just type Types.SOME_TYPE_1.instantiate(); then go to the start of the line, hit Ctrl2 + L ("Assign to loccal variable") and Eclipse replaces the AType<String> instantiate = part for you.
1If your users don't know the right type for the setValue method, then you are asking the wrong question. In that case you should have asked something like "How to design a Generic safe conversion facility?".

Maybe using a setValue method like this:
public void setValue(Object value) {
if (value == null)
throw new IllegalArgumentException("Value is mandatory.");
this.value = (T)value;
}
Although you will have an unchecked cast.
Hope this helps

Related

Is there some sort of generic bound in Java?

I am defining a type Option<T> in Java that should behave as much as possible as Rust's equivalent.
It has a method, Option::flatten, that is only implemented if the inner T is some other Option<T>. I am thinking of something like this:
public class Option<T> {
/* fields, constructors, other methods */
#Bound(T=Option<U>)
public <U> Option<U> flatten() {
if (isNone()) return None();
else return this.unwrap();
}
}
But the syntax is of course completely fictional. Is there some way to make this work in Java? I know static methods are an option, but they can't be called like a normal method which is the only goal of this type.
This is not supposed to be a standalone thing, but rather a part of a larger Java implementation of Rust iterators I'm currently working on.
The problem with trying to come up with a non-static method such as flatten is that in Java one cannot conditionally add more methods to a class based on whether the type parameter of the class fulfills a certain constraint.
You can, however, make it a static method and constrain its arguments to whatever you need.
class Option<T> {
// ...
public static <U> Option<U> flatten(Option<Option<U>> option) {
if (option.isNone()) return None();
return option.unwrap();
}
}
Which would work for valid implementations of None, isNone and unwrap.
A more complete example follows.
public static class Option<T> {
private final T value;
private Option(T x) {
this.value = x;
}
public static <T> Option<T> of(T x) {
java.util.Objects.requireNonNull(x);
return new Option<>(x);
}
public static <T> Option<T> None() {
return new Option<>(null);
}
public T unwrap() {
java.util.Objects.requireNonNull(this.value);
return this.value;
}
public boolean isNone() {
return this.value == null;
}
public static <U> Option<U> flatten(Option<Option<U>> option) {
if (option.isNone()) return Option.None();
return option.unwrap();
}
#Override
public String toString() {
if (this.isNone()) {
return "None";
}
return "Some(" + this.value.toString() + ")";
}
}
Usage:
var myOption = Option.of(Option.of(5));
System.out.println("Option: " + myOption);
System.out.println("Flattened: " + Option.flatten(myOption));
Output:
Option: Some(Some(5))
Flattened: Some(5)
I think the way you want to handle this is not to actually have a flatten() method, but have different handling in your constructor. Upon being created, the constructor should check the type it was handed. If that type is Option, it should try and unwrap that option, and set its internal value to the same as the option it was handed.
Otherwise, there isn't really a way for an object to 'flatten' itself, because it would have to change the type it was bounded over in the base case. You could return a new object from a static method, but are otherwise stuck.
I want to point out some of the potential headaches and issues regarding this re-implementation of Optional<T>.
Here's how I would initially go about it:
public class Option<T> {
/* fields, constructors, other methods */
public <U> Option<U> flatten() {
if (isNone()) return None();
T unwrapped = this.unwrap();
if (unwrapped instanceof Option) {
return (Option<U>) unwrapped; //No type safety!
} else {
return (Option<U>) this;
}
}
}
However, this code is EVIL. Note the signature of <U> Option<U> flatten() means that the U is going to be type-inferenced into whatever it needs to be, not whatever a potential nested type is. So now, this is allowed:
Option<Option<Integer>> opt = /* some opt */;
Option<String> bad = opt.flatten();
Option<Option<?>> worse = opt.<Option<?>>flatten();
You will face a CCE upon using this for the other operations, but it allows a type of failure which I would say is dangerous at best. Note that any Optional<Optional<T>> can have #flatMap unwrap for you: someOpt.flatMap(Function.identity());, however this again begs the question of what caused you to arrive at a wrapped optional to begin with.
Another answer (by #NathanielFord) notes the constructor as an option, which seems viable as well, but will still face the runtime check upon construction (with it simply being moved to the constructor):
public class Option<T> {
/* fields, constructors, other methods */
public Option<T>(T someValue) { ... }
public Option<T>(Option<T> wrapped) {
this(wrapped.isNone() ? EMPTY_OBJECT : wrapped.unwrap());
}
public Option<T> flatten() {
return this; //we're always flattened!
}
}
Note as well, the re-creation of Optional<T> by
#E_net4thecommentflagger has the potential for a nasty future bug: Optional.ofNullable(null).isNone() would return true! This may not be what you want for some potential use-cases, and should #equals be implemented in a similar manner, you'd end up with Optional.ofNullable(null).equals(Optional.None()), which seems very counter-intuitive.
All of this to say, that while Rust may require you to deal with these nested optionals, you are writing code for Java, and many of the potential restrictions you faced before have changed.

Can the compiler verify a generic type of an object through a generic method?

First of all, sorry for the bad title. I don't know how to describe the problem in a few words (maybe not even in many)...
I am refactoring some settings in our system to be more abstract. The current solution has multiple tables in the DB, one for each settings area. In order to add a new setting, you'll need to extend the schema, the hibernate class, all transfer object classes, getters/setters, etc. I felt that this is violating OCP (open-closed principle), thus the refactoring.
I've spent some time coming up with ideas on how to implement such an abstraction. My favourite idea so far is the following:
1 enum for each settings area
1 enum value for each setting
Each setting is a SettingsDefinition<T> class using a generic type
A SettingsService is using static get/set methods with generic types
So for example, a settings area could be:
public enum SettingsABC{
A(new SettingDefinition<Integer>("A", 123)),
B(new SettingDefinition<String>("B", "Hello")),
C(new SettingDefinition<Boolean>("C", false));
private SettingDefinition settingDefinition;
SettingsABC(SettingDefinition settingDefinition) {
this.settingDefinition = settingDefinition;
}
public SettingDefinition getDefinition() {
return settingDefinition;
}
}
Where the SettingDefinition is the following:
public class SettingDefinition<T> {
private String name;
private T defaultValue;
public SettingDefinition(String name, T defaultValue) {
this.name = name;
this.defaultValue = defaultValue;
}
public String getName() {
return name;
}
public T getDefaultValue() {
return defaultValue;
}
}
And the service to get/set the values would be:
public class SettingsService {
public static <T> T getSetting(SettingDefinition setting) {
// hit db to read
// return value
}
public static <T> void setSetting(SettingDefinition setting, T value) {
// hit db to write
}
}
And the consumer would look something like this:
String value = SettingsService.getSetting(SettingsABC.B.getDefinition());
SettingsService.setSetting(SettingsABC.A.getDefinition(), 123);
My problem is that I cannot enforce a compiler type check between the generic type of the SettingDefinition inside SettingsABC and the generic type of get/set methods of the service. So in essence, I can do this:
Integer value = SettingsService.getSetting(SettingsABC.B.getDefinition());
Where B's definition is of type String.
Also, I can do this:
SettingsService.setSetting(SettingsABC.A.getDefinition(), "A");
Where A's definition is an Integer.
Is there any way to use generics to force these two different generic types match?
You can convert the enum to the class:
public final class SettingsABC<T> {
public static final SettingsABC<Integer> A =
new SettingsABC<>(new SettingDefinition<>("A", 123));
public static final SettingsABC<String> B =
new SettingsABC<>(new SettingDefinition<>("B", "Hello"));
public static final SettingsABC<Boolean> C =
new SettingsABC<>(new SettingDefinition<>("C", false));
private final SettingDefinition<T> settingDefinition;
// private constructor, so nobody else would instantiate it
private SettingsABC(SettingDefinition<T> settingDefinition) {
this.settingDefinition = settingDefinition;
}
public SettingDefinition<T> getDefinition() {
return settingDefinition;
}
}
This way individual constants will be typed. Now you can use the type arguments for SettingService as well:
public static <T> T getSetting(SettingDefinition<T> setting) {
...
}
public static <T> void setSetting(SettingDefinition<T> setting, T value) {
...
}
Although it's not an enum anymore, it can be used mostly in the same way. If you need other methods which are usually available in enum, you can mimic them like this:
public String name() {
return settingDefinition.getName();
}
#Override
public String toString() {
return settingDefinition.getName();
}
// and so on

Objectify, Key<T> is it possible? Work arounds?

What I mean by type is something that would allow me to do the following.
public class AnyObject{
List<this.type> list;
}
I know the following dosen't work.
public class AnyObject{
List<this.getClass()> list;
}
So how would I create a lets say a list, for example sake, of type of whatever this is?
--------------- UPDATE ---------------
I apologize I don't think I was clear. I seem to be getting that there is no way to escape type erasure, but if there is still away to solve my problem I will explain it better. Disclosure, this is more of an Objectify question. Sorry I have come to see that now.
Here we go, clear as I can ...
For every entity I plan to persist, in GAE datastore using Objectiy, I would like to have a method to generate an Objectify Key<?> using the id and parent field. Lets call this method generateKey(). here is how it looks.
public Key<MyEntity> generateKey() {
Key<MyEntity> key = Key.create(this.parent, MyEntity.class, this.id);
return key;
}
The problem is I have to write this exact code, more or less, for every entity I create. Actually, there is other repeated code, but my point can be made with this piece of repeated code alone.
So I tried this. I created a class called MyProjectEntity and have all my entitys extend it. Then implemented a generateKey() method using generics.
public abstract class MyProjectEntity<T, Y> {
#Id Long id;
#Parent Key<T> parentKey;
public Key<Y> generateKey() {
Key<Y> key = Key.create(this.parentKey, this.getClass(), this.id);
return key;
}
}
Then I extended all my entity classes with this new class I created called MyProjectEntity. Like such ...
#Entity
public class MyEntity extends MyProjectEntity<MyEntityParent> {...}
Sounds good, now all my entity will have a generateKey() method, well this didn't quite work. Objectify yelled at me and said IllegalArgumentException, can not declare Key of type T.
Then I tried Key<Object>, Objectify was still unpleased, Objectify said Object is not a registered entity. Should I register Object!?!? and that kinda loses the whole point to a typed key that Objectify offers.
Is there a good solution. Thanks!
-- UPDATE 2 --
Since someone pointed out Key.create(myEntity) I should point my full use ...
/**********************************************************************************************************************
* Constructors END & Identification and Relationship Methods BEGIN
**********************************************************************************************************************/
#ApiSerializationProperty(name = "id")
public String getWebSafeKey() {
String webSafeKey = getKey().getString();
return webSafeKey;
}
public void setWebSafeKey(String webSafeKey) throws BadRequestException {
try {
Key<MyEntity> key = Key.create(webSafeKey);
setKey(key);
} catch (IllegalArgumentException illegalArgumentException) {
throw new BadRequestException(ErrorMessage.INVALID_ID);
}
}
#ApiSerializationProperty(name = "parentId")
public String getParentWebSafeKey() {
String webSafeKey = parent.getString();
return webSafeKey;
}
public void setParentWebSafeKey(String parentWebSafeKey) throws BadRequestException {
if (id == null) {
try {
parent = Key.create(parentWebSafeKey);
} catch (IllegalArgumentException illegalArgumentException) {
throw new BadRequestException(ErrorMessage.invalidParentId("Property"));
}
} else {
/* Do nothing. Only set parent here if setWebSafeKey is never called, such as during a create. */
}
}
#ApiSerializationProperty(ignored = AnnotationBoolean.TRUE)
public Key<MyEntity> getParentKey() {
return parent;
}
public void setParentKey(Key<MyEntity> parentKey) {
this.parent = parentKey;
}
#ApiSerializationProperty(ignored = AnnotationBoolean.TRUE)
public Key<MyEntity> getKey() {
Key<MyEntity> key = Key.create(parent, MyEntity.class, id);
return key;
}
public void setKey(Key<MyEntity> key) {
id = key.getId();
parent = key.getParent();
}
public boolean webSafeKeyEquals(String webSafeKey) {
boolean equals;
if (id !=null & parent !=null) {
equals = getWebSafeKey().equals(webSafeKey);
} else {
equals = false;
}
return equals;
}
/**********************************************************************************************************************
* Identification Methods END & Other Getters and Setters BEGIN
**********************************************************************************************************************/
All this has to be inserted for every entity I create with MyEntity replaced for the actual entity name. It's not just typing. This code doesn't belong in the entity class, but rather in some abstract parent. If I could have only code unique to a particular entity in the class, my model would be cleaner, and easier to expand. Thanks again.
This would not make sense. Consider: you would never know what the type of list is. Suppose that list is used in some method of some class, it could always be that this is an instance of a subclass. So the parameter of List in the type of list can never be assumed in any code. If it can never be known, then what is the point of it? You would just use List<?>.
Generics is a purely compile-time thing. Therefore, it is meaningless to depend on the runtime class of something.
I suggest that you have
public class AnyObject<T> {
List<T> list;
}
and any class Foo which wants to have list be a List<Foo>, for example, should just implement or inherit from AnyObject<Foo>.
This does not make sense List<this.getClass()> list; as the type parameters are compile time thing in java. This information is erased at runtime.
Without being familiar with Objectify, just generics, the thing I see is that Key.create is supposed to itself take a generic argument <T> for the type of returned Key. So you would be supposed to do the following when you call the method in the superclass:
Key<Y> key = Key.<Y>create(this.parentKey, this.getClass(), this.id);
You may only simply have to do that to fix the error (and should be doing it anyway). Otherwise Key.create will try to instantiate a new Key<Y> and although it is more or less valid to not declare a type argument when a method asks for one, apparently Key.create may not like that.
I think you should also take another look at your Ts and Ys because it appears you are mixing them. Right now you are handing Key.create a Key<T> as a parameter but wanting to return a Key<Y>. Also if you declare your class as having <T, Y> it should be illegal to extend it with only <MyEntityParent>.
Looking at your code I think what you are trying to do is create Key of the same class as the method you are calling it from. IE class generateKey in MyEntity should return a Key<MyEntity>. I think the proper way to do this would be like so (which is valid):
public abstract class MyProjectEntity<T, K> {
Long id;
Key<K> parentKey;
public Key<K> generateKey() {
return Key.<K>create(parentKey, this.getClass(), id);
}
}
public class MyEntity extends MyEntityParent<MyEntityParent, MyEntity> {
/*
* K is now MyEntity and parentKey is a Key<MyEntity>
* generateKey now does the following:
*
* public Key<MyEntity> generateKey() {
* return Key.<MyEntity>create(parentKey, MyEntity.class, id);
* }
*
*/
}
It just seems like your example that doesn't work is giving the error because you aren't declaring the types properly. But it is hard to tell because it is unclear what your T and Y are supposed to be. You only show one of the types being declared and at least in your generateKey method you are handing Key.create a Key<T> but wanting to return a Key<Y>.
Or perhaps you should take a look at Registering Entities in the Objectify API. IE it seems you might be supposed to do something like this and that is a possible reason you are getting the error:
static {
ObjectifyService.register(MyEntityParent.class);
}
But anyway in the world of Java generics you really ought to be able to do something like this without any gymnastics unless something else is going on. The nature of erasure is that you can't find out the type at runtime but the type is essentially "known" because all instances of T are replaced with the argument type.
public abstract class MyProjectEntity<T> {
Key<T> parentKey;
}
becomes
public class MyEntity extends MyProjectEntity<MyEntityParent> {
Key<MyEntityParent> parentKey;
}
You can't find out whether or not parentKey is of Type <MyEntityParent> but it is of that type. You can obviously see this with something like a java.util.List where if you do the following:
List<Double> doubleList = new ArrayList<Double>(0);
doubleList.add("a string");
You will get the following if you ignore the compiler errors and try to run the program anyway:
Uncompilable source code - Erroneous sym type: java.util.ArrayList.add
java.lang.RuntimeException: Uncompilable source code - Erroneous sym type: java.util.ArrayList.add
Because the list does "only hold" instances of Double. This situation could be compared to an anonymous class where that instance of an ArrayList's add method now officially takes a Double as an argument. It is uncompilable because I just tried to do this:
public void add(Double element) {
// add the element to the array
}
list.add("a string");
Which is obviously illegal. This ArrayList's underlying array is still an Object[] but the methods will be changed to reflect the type and safely make sure the array only holds Double elements at runtime.
So I would recommend taking a look at the things I mentioned because it appears that there's more than one problem unless you've omitted relevant code.
I think I understand your problem and here is how you could do it. The trick is to pass the subclass as a generic parameter of the parent class:
class Parent<T> {
T doStuff() {
T res = null;
// res = ..... this.getClass() is ok...
return res;
}
}
public class SelfGerenic extends Parent<SelfGerenic> {
}
public class OtherSubClass extends Parent<OtherSubClass> {
}
If I got you right, you're looking for something like this:
public class Test {
private int id;
public Key<Test> getKey() {
return createKey(id, this.getClass());
}
public static <T> Key<T> createKey(int id, Class<? extends T> clazz) {
return new Key<T>(clazz, id);
}
private static class Key<T> {
private final Class<? extends T> clazz;
private final int id;
private Key(Class<? extends T> clazz, int id) {
this.clazz = clazz;
this.id = id;
}
private int getId() {
return id;
}
private Class<? extends T> getClazz() {
return clazz;
}
}
public int getId() {
return id;
}
}
It is not possible to replace Test here: public Key<Test> getKey() {!
This is because getKey() always returns Key. It can not return Test.
So basically no, there is no way to change this behaviour. Also there is no way to get the generic type of the "current" class. This is some kind of limit of the java generics :P
You could remove the generics here, so you do not have to implement getKey() every time.
public class Test {
private int id;
public Key getKey() {
return createKey(id, this.getClass());
}
public static Key createKey(int id, Class clazz) {
return new Key(clazz, id);
}
private static class Key {
private final Class clazz;
private final int id;
private Key(Class clazz, int id) {
this.clazz = clazz;
this.id = id;
}
private int getId() {
return id;
}
private Class getClazz() {
return clazz;
}
}
public int getId() {
return id;
}
}

Map with custom value object

I want to have a Map object to contain specific value types.
Map<String,Object> foo = new HashMap<String,Object>();
foo.put("1",new Integer(1));
foo.put("2", new String("hello"):
for (Map.Entry<Integer, Integer> entry : foo.entrySet()) {
if(entry.getValue() instanceof String) {
//do something
}
else if(entry.getValue() instanceof Double) {
//throw Exception
}
}
You can see what i am trying to accomplish. I need specific Value types in my Map object so i don't have to put lots of if/else statements. How do i accomplish this ?
If you don't like the idea of having multiple maps, you can use the nice OOP feature called overriding.
see Override Methods for more information.
I would probably use composition pattern to wrap the value types and then define a interface for the action depending of the value types.
public abstract class ValueType {
public abstract void valueSpecificAction();
}
public class DoubleValueType extends ValueType {
private Double value;
public DoubleValueType(Double value) {
this.value = value;
}
public void valueSpecificAction() {
//do double specific actions
}
}
public class StringValueType extends ValueType {
private String value;
public void valueSpecificAction() {
//do string specific actions
}
}
You can then add the objects of type ValueType in the maps.
Map<String,ValueType> foo = new HashMap<String,ValueType>();
foo.put("key1", new DoubleValueType(42.0));
later you can get the value, which has the type ValueType with the public method valueSpecificAction() which do the stuff depending on the specific type.
I don't see how you can avoid to have an if-else somewhere in general.
Jan's solution would spare you the if-else only if the value types can be determine at compulation-time. That is the case in the toy example above but is it so in your actual problem?
If it is a matter of making the code look cleaner you can always hide the if-else and conversions in a set of private methods.

Emulate C++ enum integer with Java Enums

I'm attempting to translate some C++ code into Java. I'm looking for the best way to emulate this type of C++ paradigm in Java -- I think enums are probably the answer but I'm open to anything
C++ code:
typedef UInt32 Type;
enum { UNKNOWN, QUIT, SYSTEM, TIMER, LAST }
...
Type newType = UNKNOWN;
Type nextType = LAST + 1;
...
// "Register" the new type
newType = nextType;
nextType++;
...
switch (newType) {
case UNKNOWN:
case QUIT:
...
case LAST:
// Ignore unset or predefined types
default:
// Some type other than the predefined. Do something special
Essentially I'm looking for a way to "expand" the values of a Java enumeration.
enum Type { UNKNOWN, QUIT, SYSTEM, TIMER, LAST }
doesn't quit cut it.
I like the idea of making a new object for strong typing.
Again, I'm looking for the best pattern to use here. I could easily float by with a few public static final int UNKNOWN, etc
One advantage of Java enums is, that they are essentially objects like all others. In particular, you can have methods on the constants, and make your enum implement interfaces:
interface Useful {
void doStuff();
}
enum Foo implements Useful {
DEFAULT {
public void doStuff() {
...
}
},
MAGIC {
public void doStuff() {
...
}
}
}
So, instead of taking arguments of the enum type, your methods could accept any implementation of the interface, and in particular, provide the default stuff by having the enum constants implement whatever is necessary.
They can also have members:
enum Explicit {
FIRST(0), SECOND(1), THIRD(2);
private final int value;
private Explicit(int v) {
this.value = v;
}
}
Note, that constants have an internal numeric value (reflecting the position of the constant among its peers) which is accessible using the ordinal method:
assert Explicit.FIRST.ordinal() == 0;
But relying on this is a little bit dangerous. If you are going to (say) persist enum constants in a database, then the following change would break the code:
enum Explicit {
FIRST(0), NEWELT(-1), SECOND(1), THIRD(2);
private final int value;
private Explicit(int v) {
this.value = v;
}
}
when using ordinal values. For this reason, the serialization machinery uses the constant's name instead of its position when serializing enum values.
So:
Type.X + 1
would be
Enum.values()[Enum.X.ordinal() + 1]
and the switch could be modelled using interfaces implemented by the enum itself (you can use enums in switch statements in Java, but often, making the enum implement the necessary code yields more maintainable code)
If each value of Type is just an int, then I'd probably go for a bunch of static final ints in a class.
Just for the record, something like this works more or less type-safely. I can't determine if the complexity is warranted without knowing more about the problem.
public class abstract Type
{
public static final Type UNKNOWN = registerStd("UNKNOWN");
...
public static final Type TIMER = registerStd("TIMER");
// use this to keep track of all the types
private static final List<Type> REGISTERED = ...
//This will do your switch statement for you, implemented by
// anonymous subclasses
public abstract void dispatch(Type onMe);
// here's how you make the standard ones
private static Type registerStd(String name)
{
Type heresOne = new Type(name)
{
// note, it's a no-op
public void dispatch(DoStuffer algorithm) {}
};
REGISTERED.add(heresOne);
return heresOne;
}
//here's how you make the non-standard ones
public static Type registerNew(String name)
{
Type heresOne = new Type(name)
{
public void dispatch(DoStuffer algorithm) {algorithm.execute(this)}
};
REGISTERED.add(heresOne);
return heresOne;
}
}
public interface DoStuffer
{
public void execute(Type onMe);
}
//Then your code becomes
Type newType = Type.registerNew("NewType");
newType.dispatch
(
new DoStuffer()
{
public void algorithm(Type forMe) { ... }
}
);
Maybe this is a little esoteric. But it does allow for "easy dispatch" at the caller site and an extensible enum, in some sense.

Categories

Resources