Need help in tweaking enum lists - java

I have public enum like below:
public enum stringList1 {
string1,
string2,
string3,
string4,
string5,
}
This enum "stringList1" is referred in all over the code more than 100 places. Currently I have requirement to support below list as well.
enum stringList2 {
string1,
string2,
string6,
}
Actually code should pick either stringList1 or stringList2 depending on a condition.
I know enums can not be changed, also changing all over the code to put condition to pick right enum list is very difficult.
Here refactoring will not help, I need to pick either stringList1 or stringList2 at runtime, but currently stringList1 is referred all over the code. One option is putting the condition in all the places stringList1 is referred to pick stringList1 or stringList2, but this is very difficult.
UseCase: This is for backward compatibility, stringList1 for current version, say v2, stringList2 for v1. Data can be operated by V1 binary and V2 binary. Currenty if user tries to create data using V2 binary, it will create data, which is not understood by V1. So in version 2 binary I need to check, if version is 1, I need to used stringList2 else stringList 1
Lets say method method1(), which will create some predefined values with the help of stringList1, here user has access to method1() don't have access to enum values.
so user just simply call method1, I need to find the version, depending on the version, I have to use stringList1 or stringList2 but the problem is stringList1 is used all over the code :( as Classname.stringList1.values() or Classname.stringList1 in all over the code, for some internal processing
Please advise me how can I proceed in this case.
Thanks in Advance,
Ashok

maybe using the refactor function (from eclipse - i guess netbeans and others have something like that too) and renaming could help
public enum stringList {
stringList_string1,
stringList_string2,
stringList_string3,
stringList_string4,
stringList_string5,
}
public enum stringList2 {
stringList2_string1,
stringList2_string2,
stringList2_ string6,
}
hope this helps you :)

In your IDE you should be able to use refactoring to rename one instance and only rename just that instance (without change other enums)

If you need to keep the enums separate, i.e. not just add all the values together, you could make them implement an interface, e.g.
public interface stringListCommon {
}
public enum stringList implements stringListCommon {
..
}
public enum stringList2 implements stringListCommon {
..
}
This will allow you to pass these enums around with the same methods.

The better and modern way(I think you know enum is comming from C) is to structure special class for this operation. I will wrote you an example with playing card suits.
public class Suit {
private final String name;
private Suit(String name);
public static final Suit CLUBS = new Suit("clubs");
public static final Suit DIAMONDS = new Suit("diamonds");
public static final Suit HEARTS = new Suit("hearts");
public static final Suit SPADES = new Suit("spades");
/*other code is what you need you need(either final Suit[]
or final unmodifiableList to store values*/

Related

Java - Create a Card object without enum, with compilation time type checking

I want to create a class that would provide me with a card
i.e
Card c = new Card(1) // where the builder should get values 1 to 4, representing 1=spade, 2=heart, 3=diamond, 4=clubs
c.getType() // spade
I want the class to check during compilation time whether the card is of type spade, heart, diamond or club, and if not - it'll raise an error and won't let me compile.
The class should NOT use enum
Edit:
This question is asked solely for the purpose of understanding how would one answer that kind of question in the pre-enum era. I was asked this question during a job interview a couple of years ago
Update: there is no good way to enforce the value being between 1 and 4 (perhaps using some custom complex compile time annotation would work but I doubt it) as has been pointed out. This is just one way to enforce not allowing Card to be instantiated without using enums, and it implicitly keeps the value between 1 and 4 since each subclass sets its own value.
One way would be to make Card abstract (could also use an interface):
public abstract class Card {
// should be 1 to 4
protected int value;
public int getValue() {
return value;
}
}
Then create child classes for each type:
public class Spade extends Card {
public Spade() {
this.value = 1;
}
}
public class Heart extends Card {
public Spade() {
this.value = 2;
}
}
This would throw a compile time error if someone tried to create a Card directly:
Card c = new Card();
Before enums existed, this is how most Java code handled enumerations: using a preset list of integers that were very error prone and not easily enforceable. It was a dark time, let's not go back :)
Java has no features to make new Card(4) compile fine, and new Card(5) to cause a compile-time failure. Can't be done.
The obvious thing to use here is enums. If you have some crazy requirement that prevents you from using it, well, do the usual thing when faced with crazy requirements: Accept that code style and tooling is going to suffer.
You could make subtypes for each suit (as shown by the answer from #Mike), but that isn't really suitable. subclasses are meant to represent different behaviour, and there is nothing different about a Hearts vs. a Spade.
Just so we're clear, let me repeat that: What you ask for is impossible.
Thank you all for your answers, and thank you #JoachimSauer, I checked the link you gave me about pre 1.5 and the Type Safe Enum Pattern and I managed to get an answer I like:
class Solution {
public static class Card {
public static Card SPADE = new Card();
public static Card HEART = new Card();
public static Card DIAMOND = new Card();
public static Card CLUB = new Card();
public Card() {
}
}
public static void main(String[] args) {
Card c = new Card().SPADE; // Only Spade, Heart, Diamond, Club can be "manufactured"
}
}
I know that it's not exactly what I've described, but only upon reading I understood what exactly I had in mind
of course thanks to #Mike for giving me the answer for what I managed to describe

How can I wrap existing static constants into an enumeration?

I'm porting my Minecraft block protections plugin from Bukkit to Sponge so I can add mods with SpongeForge. Bukkit uses a Material enum to identify all the valid block types in the game. All my protections are specified in the config.yml file like this:
CHEST:
Price: 0.75
InteractMember: R
...
With an enum it was very easy to take the keyname in the config file, CHEST, and get the actual enum value with Bukkit.getMaterial(String name). Unfortunately, Sponge refuses to use enums anywhere in their code, so their list of block types is a class containing nothing but static final int constants, which I cannot iterate through or retrieve by name. I tried reflection..
HashMap<String,Integer> blockTypes = new HashMap<String,Integer>();
for(Field field, BlockTypes.class.getFields())
blockMap.put(field.getName(), field.getInt(null));
But I can only get the int values for the constants. I need the constants themselves to use in the code, and I can't find any way to do that without making my own enum wrapper for the static constants:
public enum Blocks {
ACACIA_FENCE(BlockTypes.ACACIA_FENCE),
ACACIA_STEPS(BlockTypes.ACACIA_STEPS),
...
YELLOW_FLOWER(BlockTypes.YELLOW_FLOWER);
private final BlockTypes type;
Blocks(BlockTypes type) {
this.type = type;
}
public BlockTypes getType() { return type; }
public static BlockTypes getByName(String name) {
// retrieve enum by name
}
}
Am I really stuck doing this or is there another way I'm missing?
Sponge doesn't use enums for a reason: since you can add other mods, constants would have to be dynamically added (which isn't possible), and assuming blocks in the vanilla game are the only blocks isn't valid. Support for other mods is one of the main goals of the sponge API.
If your goal is to get a list of all valid BlockTypes in the game, you should use the GameRegistry:
// Getting a list of all types
Collection<BlockType> types = Sponge.getRegistry().getAllOf(BlockType.class)
for (BlockType type : types) {
System.out.println(type.getName());
}
// Getting a single type by name
Optional<BlockType> type = Sponge.getRegistry().getType(BlockType.class, "minecraft:chest");
if (!type.isPresent()) {
// show some error, as the given type doesn't exist
} else {
return type.get();
}
You should be able to use BlockType as a key in a map, or alternatively the String id. You shouldn't need to make an enum for it (and can't do it automatically).
Worth noting that you're using reflection wrong in your example as well, but I don't think it's too important to explain how it needs to be used right now as that's the wrong approach.

Using Enum to hold object constants

So I've learned a bunch recently so I'm going back and sort of refactoring the homeworks from a previous course that I took to implement them using good practices. One homework had us implement a Planner object that contained an array of Course objects. I'm trying to create some Course constants so that I can access some popular Courses without having to create brand new Objects every time and so I can easily access them without going through the Course building process. I don't have much experience with enums and I can't seem to find anything on how I can actually use an Enum to store constants that are Objects. I originally wanted to make them constants in the Course class but Effective Java insists enumns should be used in such a situation. Does my implementation make sense at all? How should I go about making this enum that contains Course constants so I can actually retrieve them? I use the Builder method for creating a Course.
public enum Courses {
CSE_114, CSE_214, CSE_219, CSE_215;
private final static Course CSE_114_COURSE = new Course
.Builder("Computer Science 1", "Paul Fodor", 114)
.section((byte)1).department("CSE").build();
private static final Course CSE_214_COURSE = new Course
.Builder("Data Structures", "Ahmad Esmaili", 214)
.section((byte)1).department("CSE").build();
private static final Course CSE_219_COURSE = new Course
.Builder("Software Development", "Richard McKenna", 219)
.section((byte)1).department("CSE").build();
private static final Course CSE_215_COURSE = new Course
.Builder("Foundations of CS", "Paul Fodor", 215)
.section((byte)1).department("CSE").build();
public static Course get(Courses c) {
switch(c) {
case CSE_114: return CSE_114_COURSE;
case CSE_214: return CSE_214_COURSE;
case CSE_219: return CSE_219_COURSE;
case CSE_215: return CSE_215_COURSE;
default: throw new IllegalArgumentException("Course does not exist.");
}
}
}
You can actually treat an enum like an object:
public enum Course {
CSE_114("Computer Science 1", "Paul Fodor");
public final String room;
public final String lecturer;
private Course(room, lecturer) {
this.room = room;
this.lecturer = lecturer;
}
}
Because it is an enum, all values must be known at compile time. This is enforced by the Java language, which requires that the enum constructor is private.
While this would work for your situation, I don't recommend it - in fact, I don't recommend using an enum at all. An enum represents a fixed, known set of values. If you want to create more courses at runtime, then the enum is incomplete, and that contradicts the definition of an enum.
Instead, I suggest you use a CourseManager. Create one class, which holds the collection of all known courses. Then, when you need a course, you request it by name.
Course cs114 = courses.get("CS 114");
You could also take it one step further, by instantiating the CourseManager from a file, which contains a list of courses in a basic format like JSON.
That's not a bad implementation but I think a have a bit better solution: add an abstract method to your enum definition.
public enum Courses {
CSE_114 {
public Course getCourse() {
return CSE_114_COURSE;
}
}
...
private final static Course CSE_114_COURSE = new Course
.Builder("Computer Science 1", "Paul Fodor", 114)
.section((byte)1).department("CSE").build();
...
public abstract Course getCourse();
}
That way you can access to any course object vía Courses.CSE_114.getCourse() and you are also forcing that every enum has a course (imagine that you add an enum constant but forgot to add it in the get method. The way I proposed makes that scenario non sense)
You are mixing enum constants with static constants. Get these courses and selection of courses out of the enum class. Or you can use something like Andrew Williamson showed. Just don't mix the static constants with the enums. Static constants don't belong to the enum class. Enum class has a fixed set of values and that is where its role ends. All the other logic using these enums should be done somewhere else

Require input regarding JAVA ENUMS

Consider the following
public enum tc implements {
NORESULTS(0), GOOD_RESULTS(1), EXCELLENT_RESULTS(2), NO_DATA_AVAILABLE(5), SOME_OTHER_VALUE(4);
private final Integer value;
// Code for the constructor, getters and setters for the value****
The enum tc values correspond to the testValue in the below class.
public class TestData {
private int testID;
private String testName;
private int testValue;
....
...
}
In the Results class, the TestDataList has to be sorted by a different order of ranking rather than testValue.For example Excellent followed by Good Results followed by NoResults etc..
public class Results {
List<TestData> TestDataList = getTestData();
I can code for the comparator etc..the question is since I require a different ordering for the enums which of the following two options is better
a) add private int rankTestValue in the enum tc. This option may require that I have to write a method getRank(int value) that would return the corresponding rankTestValue based on the value.
OR
b) add in Results class a map Map tcRankMap = new HashMap();. Populate this map with key values like (2,1) (1,2) corresponding to (enum values, ranking).For example (2,1) would be Excellent_Results has first ranking etc.
Which of these two options would be better. If there are better solutions then please let me know.
Option (a) looks better and according to Object Oriented Analysis and Design.
The good news is that the is a question of implementation detail which can be encapsulated into your Comparator anyway, so it doesn't matter so much.
As for style and readability, I would prefer (a), but it's down to personal preference.
There is also a solution (c) - use the ordinal(), and then sort them according to rank. Just add a comment to make it clear
public enum tc implements {
// NB: enum values are sorted according to rank
EXCELLENT_RESULTS(2),
GOOD_RESULTS(1),
NORESULTS(0),
NO_DATA_AVAILABLE(5),
SOME_OTHER_VALUE(4);
private final Integer value;
// Code for the constructor, getters and setters for the value****
}
Your first option would look like this:
enum TestScore {
EXCELLENT(5),
NO_RESULT(2),
POOR(1);
private final int order;
private TestScore(int order) {
this.order = order;
}
public int compareOrderTo(TestScore other) {
return this.order - other.order;
}
}
You could then add a comparison method to TestData
public int compareTestScore(TestData other) {
return this.testScore.compareOrderTo(other.testScore);
}
And sort your list with:
Collections.sort(testData, TestData::compareTestScore);
The problem with this is that the order field is really completely arbitrary and needs to be updated each time you add a new entry. However that's definitely better and more explicit than using the natural ordering of the enum (i.e. it's ordinal value which should be entirely incidental to avoid fragility).

How can I simulate Enums that can be expanded at Runtime in Java?

I am writing a program to simulate cities from a game called Civilization 4. In order to do this I have several Enums to represent types of terrain, resources, improvements etc for each plot owned by said city.
The problem is I want to program to be compatible with Fan made mods which may add things to the Game that need to be accepted into my independant utility. So I thought of creating a Enum style class to hold the new types defined by the loaded mods (as Enums cannot be changed at runtime) which is created during runtime when the user enters in a mod to be loaded (which is a txt file that is parsed to read the new additions)
So is there a way to simulate Enums that are created and added to at runtime? I take it static member variables cannot be used as they are done before runtime...
You can make a enum implement an interface.
This way you can have your defined values in the enum, but new values can be any class which implements the interface.
An alternative is that you generate or load the enum at runtime using a byte code generator or the Compiler API. I wrote a library to make it easier to take a String and compile&load it.
http://vanillajava.blogspot.co.uk/2010_11_01_archive.html
Well, enums in Java are simply classes where the language guarantees that the set of known objects is known and limited at compile-time. If you want to add new enum literals at runtime, you end up with regular classes.
The beauty of enums is that you can write human readable names in code that are compiled as numbers behind the scenes, because computers like numbers better. Take for example this enum:
enum Season { WINTER, SPRING, SUMMER, AUTUMN }
Behind the scenes WINTER might be 0 (zero), SPRING is 1 etc.
To replicate this behaviour in runtime code you could create a list of strings, like this:
List<String> seasons;
seasons = new ArrayList<String>();
seasons.add("Winter");
seasons.add("Spring");
...
That way you can reference the items as numbers, such as seasons[1] would equal "Spring".
This answer is just one of many ways to approach this question.
By default, enum types have only a set number of values. The values in an enum type are actually declared as static final, and there's no way to add more on runtime.
That being said, there are other patterns you can use to implement what you want. Let's take a look at using an interface and a registration system. We'll start with the Terrain interface:
public interface Terrain {
int getId();
String getName();
int getSightBonus();
}
Now an enum, DefaultTerrain:
public enum DefaultTerrain implements Terrain {
PLAINS(0, "Plains", 1),
HILLS(1, "Hills", -1),
MOUNTAINS(2, "Mountains", -2);
private int id;
private String name;
private int sightBonus;
private DefaultTerrain(int id, String name, int sightBonus) {
this.id = id;
this.name = name;
this.sightBonus = sightBonus;
}
public int getId() {return id;}
public String getName() {return name;}
public int getSightBonus() {return sightBonus;}
}
And a registration class, which can be either a static utility class or a singleton.
public class TerrainManager {
private static Map<Integer, Terrain> terrainsById = new HashMap<>();
static {
for (DefaultTerrain terrain : DefaultTerrain.values())
register(terrain);
}
public static void register(Terrain terrain) {
Integer id = terrain.getId();
if (terrainsById.contains(terrain.getId()))
throw new IllegalArgumentException("Terrain with id already exists: " + id);
terrainsById.put(id, terrain);
}
public static Terrain getTerrainById(int id) {
return terrainsById.get(id);
}
public static Set<Terrain> getAllTerrains() {
return new HashSet<Terrain>(terrainsById.values());
}
}
This last class is where the magic happens. Presumably the modders will have some kind of identifier in the game's world definition to say "use this tile," right? In this case, I've called it an integer, id, but really it could be any type, just modify the Map accordingly. In the map-loading code, just use the ID in the world definition to look up the Terrain. When a modder adds a new Terrain, they just need to implement Terrain and register it with TerrainManager.
The static initializer makes sure that your DefaultTerrain objects are added before anything else is added. If you use a singleton, this could be put into the class constructor instead.
Use this pattern for your different enum types that you want users to add to. You could also use it for pretty much any other type as well besides enum.
You will need CGLIB, http://cglib.sourceforge.net/

Categories