I got an application in GWT that will consist of a listbox with more than 50 items. When selecting one I'll go to the corresponding place.
To avoid hard coded values and to share with the server part, I created an Enum lets call it TableEnum
So Table Enum is composed of a key and a displayName.
Which I then use to fill my combo. Once the selection is done, I got the value and so need to get the Place to go to.
That's why I created a factory that take the value of the selection and return a Place object.
First solution I was about to do is transform my value in TableEnum object et do a switch/case creating the correct associated Place.
But I was also thinking about adding the Place directly as a field of my Enum. This would avoid the switch/case and I would only need to do tableEnum.getPlace().
But I'm not really confident that this can be called a good practice. In my head Enum were simple objects not really knowing what was around them.
Thanks for information
Enums in Java are not just a placeholder for integers, or just some constants (such as some languages). Enums are classes with fixed number of objects, so it is a good practice to give your enums more brain, and always forget about lots of if/else or *switch/case*es.
If each item in the enum is associated to only one place, you can create an abstract method on your enum to get the place.
public enum TableEnum {
FIRST(){
public Place getPlace(){ return new FirstPlace(); }
},
SECOND(){
public Place getPlace(){ return new SecondPlace(); }
};
public abstract Place getPlace();
}
Later, you can access the place like so:
TableEnum t = ...
t.getPlace()
like Danny Kirchmeier's, but maybe less code:
enum Table {
first(Place.place1), second(Place.place2);
Table(Place place) {
this.place = place;
}
final Place place;
}
maybe your place should be an enum?
Related
I have this code, which obviously doesn't look nice - it seems all the if-else can somehow be avoided.
if(sortBy.equals("firstName"))
personList.sort(Comparator.comparing(Person::getFirstName));
else if(sortBy.equals("lastName"))
personList.sort(Comparator.comparing(Person::getLastName));
else if(sortBy.equals("age"))
personList.sort(Comparator.comparing(Person::getAge));
else if(sortBy.equals("city"))
personList.sort(Comparator.comparing(Person::getCity));
else if(sortBy.equals("state"))
personList.sort(Comparator.comparing(Person::getState));
else if(sortBy.equals("zipCode"))
personList.sort(Comparator.comparing(Person::getZipCode));
the function takes sortBy, which is the name of one of the attributes of a Person, and applies a sorting to a personList based on that field. How can I avoid the if-else and write a better looking, possibily one line code?
Currently I have found that I can use a HashMap to create a mapping between a field name and a corresponding comparator.
map.put("age", Comparator.comparing(Person::getAge));
map.put("firstName", Comparator.comparing(Person::getFirstName))
...
And use personList.sort(map.get(sortBy)).
But still felt like it can further be improved without an extra step, to the point where it follows the open-closed principle, and adding a new field to Person would not need us to modify the code. I'm looking for something like
personList.sort(Comparator.comparing(Person::getterOfField(sortBy)))
UPDATE-1
For now, I decided to stick with using a Map<String, Function<Person, Comparable<?>> and I do not like to consider reflection based solutions. But still searching if I can find a similar way as this one where sort is a parameter.
UPDATE-2
I think a one-liner is not a good solution, cuz you wouldn't get a compile time error if one of the fields does not implement Comparator.
In general java doesn't want you to work with it this way1; it is not a structurally typed language, and unlike e.g. javascript or python, objects aren't "hashmaps of strings to thingies".
Also, your request more fundamentally doesn't add up: You can't just go from "field name" to "sort on that": What if the field's type isn't inherently sortable (is not a subtype of Comparator<Self>?)
What if there is a column in whatever view we're talking about / config file that is 'generated'? Imagine you have a field LocalDate birthDate; but you have a column 'birth month'2. You can sort on birth month, no problem. However, given that it's a 'generated value' (not backed directly by a field, instead, derived from a calculation based on field(s)), you can't just sort on this. You can't even sort on the backing field (as that would sort by birth year first, not what you want), nor does 'backing field' make sense; what if the virtual column is based on multiple fields?
It is certainly possible that currently you aren't imagining either virtual columns or fields whose type isn't self-sortable and that therefore you want to deposit a rule that for this class, you close the door on these two notions until a pretty major refactor, but it goes to show perhaps why "java does not work that way" is in fact somewhat 'good' (closely meshes with real life concerns), and why your example isn't as boilerplatey as you may have initially thought: No, it is not, in fact, inevitable. Specifically, you seem to want:
There is an exact 1-to-1 match between 'column sort keys' and field names.
The strategy to deliver on the request to sort on a given column sort key is always the same: Take the column sort key. Find the field (it has the same name); now find its getter. Create a comparator based on comparing get calls; this getter returns a type that has a natural sorting order guaranteed.
Which are 2 non-obvious preconditions that seem to have gotten a bit lost. At any rate, a statement like:
if(sortBy.equals("firstName"))
personList.sort(Comparator.comparing(Person::getFirstName));
encodes these 2 non-obvious properties, and trivially, therefore means it is also possible to add virtual columns as well as sort keys that work differently (for example, sorts on birth month, or, sorts on some explicit comparator you write for this purpose. Or even sorts case insensitively; strings by default do not do that, you'd have to sort by String.CASE_INSENSITIVE_COMPARATOR instead.
It strikes me as a rather badly written app if a change request comes in with: "Hey, could you make the sort option that sorts on patient name be case insensitive?" and you go: "Hooo boy that'll be a personweek+ of refactoring work!", no?
But, if you insist, you have 2 broad options:
Reflection
Reflection lets you write code that programatically gets a list of field names, method names, and can also be used to programatically call them. You can fetch a list of method names and filter out everything except:
instance methods
with no arguments
whose name starts with get
And do a simple-ish get-prefix-to-sort-key conversion (basically, .substring(3) to lop off the get, then lowercase the first character, though note that the rules for getter to field name get contradictory if the first 'word' of the field is a single letter, such as getXAxis, where half of the beanspec documents say the field name is definitely XAxis, as xAxis would have become getxAxis, and the other half say it is ambiguous and could mean the field name is XAxis or xAxis).
It looks something like this:
// intentionally raw type!
Map comparators = new HashMap();
for (Method m : Person.class.getMethods()) {
if (Modifiers.isStatic(m.getModifiers()) continue;
if (m.getParameterCount() != 0) continue;
String n = m.getName();
if (!n.startsWith("get") || n.length() < 4) continue;
n = Character.toLowerCase(n.charAt(3)) + n.substring(4);
comparators.put(n, (a, b) -> {
Object aa = m.invoke(a);
Object bb = m.invoke(b);
return ((Comparable) aa).compareTo(bb);
});
}
MyClass.COMPARATORS = (Map<String, Comparator<?>>) Collections.unmodifiableMap(comparators);
Note how this causes a boatload of errors because you just chucked type checking out the window - there is no actual way to ensure that any given getter type actually is an appropriate Comparable. The warnings are correct and you have to ignore them, no fixing that, if you go by this route.
You also get a ton of checked exceptions issues that you'll have to deal with by catching them and rethrowing something appropriate; possibly RuntimeException or similar if you want to disregard the need to deal with them by callers (some RuntimeException is appropriate if you consider any attempt to add a field of a type that isn't naturally comparable 'a bug').
Annotation Processors
This is a lot more complicated: You can stick annotations on a method, and then have an annotation processor that sees these and generates a source file that does what you want. This is more flexible and more 'compile time checked', in that you can e.g. check that things are of an appropriate type, or add support for mentioning a class in the annotation that is an implementation of Comparable<T>, T being compatible with the type of the field you so annotate. You can also annotate methods themselves (e.g. a public Month getBirthMonth() method). I suggest you search the web for an annotation processor tutorial, it'd be a bit much to stuff an example in an SO answer. Expect to spend a few days learning and writing it, it won't be trivial.
[1] This is a largely objective statement. Falsifiable elements: There are no field-based 'lambda accessors'; no foo::fieldName support. Java does not support structural typing and there is no way to refer to things in the language by name alone, only by fully qualified name (you can let the compiler infer things, but the compiler always translates what you write to a fully "named" (package name, type name that the thing you are referring to is in, and finally the name of the method or field) and then sticks that in the class file).
[2] At least in the Netherlands it is somewhat common to split patient populations up by birth month (as a convenient way to split a population into 12 roughly equally sized, mostly arbitrary chunks) e.g. for inviting them in for a checkup or a flu shot or whatnot.
Assuming that the sortBy values and the corresponding getters are known at compile, this would be a good place to use a string switch statement:
Function<Person.String> getter = null;
switch (sortBy) {
case "firstName":
getter = Person::getFirstName; break;
case "lastName":
getter = Person::getLastName; break;
...
}
personList.sort(Comparator.comparing(getter));
If you use a recent version of Java (Java 12 and later) you could use a switch expression rather than a switch statement.
Function<Person.String> getter;
getter = switch (sortBy) {
case "firstName" -> Person::getFirstName;
case "lastName" -> Person::getLastName;
...
default -> null;
}
personList.sort(Comparator.comparing(getter));
Note: you should do a better job (than my dodgy code) of dealing with the case where the sortBy value is not recognized.
As keshlam suggested, I think using the reflection API is the best fitting answer to your question, but keep in mind that using it in production code is generally discouraged.
Note: if you add a new Person-attribute which isn't itself Comparable, you'll have to resort to a custom Comparator anyway. With that in mind, you might want to keep the Map<String, Comparator<?>> solution you already have.
I'm currently working on an "item drop" system, where items (and their properties) are randomly generated upon slaying an enemy. The current system I have is a hierarchy of classes, with Item being the root super class. Each class has specific properties common to all subclasses.
Ideally upon dropping an item, program will randomly select one property of the item and move down the hierarchy tree accordingly. For example, the process is as follows:
Class | Randomly Selected Property determining path in tree:
Item | ItemType -> Equipable | EquipType -> Weapon | WeaponType -> etc.
Example code:
abstract class Item
{
private Type itemType;
private String itemName;
private int itemLevel;
private Rarity itemRarity;
private boolean questItem;
private int itemPrice;
public Item(String name)
{
itemType = Type.randomize();
itemName = name;
itemLevel = randomizeLevel(pLvl, eLvl);
itemRarity = Rarity.randomize(bonus, pLvl, eLvl, iLvl);
questItem = false;
itemPrice = determinePrice();
}
}
Type is an enum deciding the next level in the hierarchy. With this system, I dont know how to let the program determine the next level, and then proceed to it.
The problem I've run into is that I've realized in order for this to work, I have to work from the bottom of the hierarchy, up. Is there a way for me to work from the top-down, meaning I can start with a basic Item class and procedurally work my way down (using the methods I created to randomly select the properties).
If not, is there a specific way I should implement this system?
You can get a list of all your enums from the enum class - the order is the order in which they are declared so if you declare them in hierarchy order, you can traverse the list however you want. The relevant oracle doc is here:
https://docs.oracle.com/javase/tutorial/reflect/special/enumMembers.html
If you want to work your way from the top down, especially if the subclass selection depends on the choices made when constructing the superclass, then it's probably much better to favor composition over inheritance. So, if for Item you have Type specific information, you could make Type contain all the properties you could possibly want, including references to subsequent objects down this (conceptual) hierarchy.
At any point, of course, you can in addition make a small class hierarchy out of Item, Type, and so on as long as you know before creating the object the specific subclass it should be. Otherwise, composition would be the only way to create your item in the fashion you describe.
You could have a static method in each class that determines the 'next transition'(thinking of the package of 'items' as a tree.
So
// untested code
String nextClass = "Item";
// define a helper method
Class x = getClass(nextClass);
while( x.hasMore() == true )
{
nextClass = x.getNextClass();
x = getClass(nextClass);
}
// reflection here on Class 'x' (whatever that is...)
Then you can use reflection to instantiate the last class.
This probably is a bad idea. But it would 'walk' a tree of well formed classes
If I understand your goal, and try to reformulate:
you have a top type: Item
you have (or create now, or further perhaps ?) subclasses of Item, by adding some property: Equipable, then Weapon, etc.
from top (Item), you want to random select a subclass (Equipable ? yes/no ? if yes => equip Type, etc.
What I suggest:
dont break notion of subclass: a parent doesnt need to know by advance his children
then take a class (1st: item, and so on), search subclasses by reflection
select between children: at this point you must define how you categorize between children: any new variable, or any variable begining by a prefix. For example, you can define final static int category_weapon=1; (or true, false, ...)
then to iterate, you go from top, get children, get these static variables and value, and choose at random between these children, and do it again
Another way to caracterize: use Interfaces (weapon, equipable, ...): the benefit is you can then apply predefined method (for weapon, ...)
Cheaper : dont use any variable, just use the name of the children class to choose the next.
A good way to choose: define precisely what you'll do with these objects.
Hope it helps.
Let's say I have the enum AccessRole with 3 declarations:
public Enum AccessRole
{
Admin,
User,
ReadOnly;
}
I use the ordinal value of this enum to denote the concept of "hierarchical" access role. For example,
if (currentRole.ordinal() < otherRole.ordinal())
{
//We know currentRole has more user rights than otherRole, because it's ordinal is smaller.
}
I don't base any rights of the ordinal, but I use the ordinal to sort and compare the enum values when necessary (for example, in the user interface, to sort all possible access roles by ordinal from "many rights" to "limited rights").
This works fine, but I'm concerned that this is not clear. If any other developer were to insert a new accessrole at the bottom of the enum instead of at the correct ordinal, the whole system is messed up.
Is there any way to deal with this? That this, impose an order on enum declarations, yet make it clear in the code semantically that the order is important/being used?
You are rightly concerned. The order of enums is often not considered important by developers.
Yet adding another artifical ordinal just make things worse, because now you have take care that the numbers are different, and the meaning of the new ordering is still not clear.
I think what would help is to move the check for privileges in the Enum. So have method in your Enum that looks like this:
public boolean includes(Role otherRole){
return (this.ordinal() < otherRole.ordinal())
}
Now you can add tests. And every developer worth her money will understand that changing the order of things in the enum is actually a change in business logic.
While the order of enums is not normally considered as being important, the way I would address this case (where it does matter) would be to put a prominent comment in the source code.
Sure, you can't protect against programmers who modify (butcher!) your code without reading the comments. But on the other hand:
they are liable to butcher your code in other ways anyway, and
ultimately, if they ignore your comments and break the code as a result, they should take the blame.
How about adding an integer attribute to the Enum and document that it is used for maintaining the hierarchy of roles. This also allows to define two roles at the same level.
I've got loads of the following to implement.
validateParameter(field_name, field_type, field_validationMessage, visibleBoolean);
Instead of having 50-60 of these in a row, is there some form of nested hashmap/4d array I can use to build it up and loop through them?
Whats the best approach for doing something like that?
Thanks!
EDIT: Was 4 items.
What you could do is create a new Class that holds three values. (The type, the boolean, and name, or the fourth value (you didn't list it)). Then, when creating the HashMap, all you have to do is call the method to get your three values. It may seem like more work, but all you would have to do is create a simple loop to go through all of the values you need. Since I don't know exactly what it is that you're trying to do, all I can do is provide an example of what I'm trying to do. Hope it applies to your problem.
Anyways, creating the Class to hold the three(or four) values you need.
For example,
Class Fields{
String field_name;
Integer field_type;
Boolean validationMessageVisible;
Fields(String name, Integer type, Boolean mv) {
// this.field_name = name;
this.field_type = type;
this.validationMessageVisible = mv;
}
Then put them in a HashMap somewhat like this:
HashMap map = new HashMap<String, Triple>();
map.put(LOCAL STRING FOR NAME OF FIELD, new Field(new Integer(YOUR INTEGER),new Boolean(YOUR BOOLEAN)));
NOTE: This is only going to work as long as these three or four values can all be stored together. For example if you need all of the values to be stored separately for whatever reason it may be, then this won't work. Only if they can be grouped together without it affecting the function of the program, that this will work.
This was a quick brainstorm. Not sure if it will work, but think along these lines and I believe it should work out for you.
You may have to make a few edits, but this should get you in the right direction
P.S. Sorry for it being so wordy, just tried to get as many details out as possible.
The other answer is close but you don't need a key in this case.
Just define a class to contain your three fields. Create a List or array of that class. Loop over the list or array calling the method for each combination.
The approach I'd use is to create a POJO (or some POJOs) to store the values as attributes and validate attribute by attribute.
Since many times you're going to have the same validation per attribute type (e.g. dates and numbers can be validated by range, strings can be validated to ensure they´re not null or empty, etc), you could just iterate on these attributes using reflection (or even better, using annotations).
If you need to validate on the POJO level, you can still reuse these attribute-level validators via composition, while you add more specific validations are you´re going up in the abstraction level (going up means basic attributes -> pojos -> pojos that contain other pojos -> etc).
Passing several basic types as parameters of the same method is not good because the parameters themselves don't tell much and you can easily exchange two parameters of the same type by accident in the method call.
Consider this situation: I've got an aquarium simulator where I have 5 different types of fishes. Different types means different attributes (speed, colour, hunger, etc). What if I want the user of my simulator to be able to create a new type of fish and give it its values for its attributes?
How is that implemented by the programmer? Do I need some kind of "event handling" that will add a specific bunch of lines of code in my "Fish" class? Is that even a valid thought?
(In case it's essential, the language is Java. And to avoid any misunderstandings and prevent comments like "is this uni work?", yes it is. But I am not looking for THE answer, I am curious about the concept.)
EDIT: Yeah, my bad that I didn't mention the interaction way: a GUI.
So, imagine a tab called "Add New Species" that has a field for every attribute of the fishes (type, speed, colour, etc). So, the user fills in the fields with the appropriate values and when he clicks on "add" the constructor is called. At least that's how I imagine it. :)
I would just use a map:
class Fish
{
Map<String,String> attributes = new HashMap<String,String>();
setBusterFish()
{
attributes.put("speed", "5");
attributes.put("colour", "red");
attributes.put("hunger", "10");
attributes.put("name", "buster");
}
}
Java is an OO language, and it deals in classes and objects. The tempting, naive solution would be to have your program deal with "classes" of fish like it deals with classes of anything, i.e. to create some Java code and let the compiler and loader introduce it into the runtime.
This approach can be made to work, with some awkwardness. Essentially your "dynamic Java classes" coding would probably end up much bigger and complicated than your assignment actually intends.
You only really need to do this if you are actually going to have different attributes (not just different values of those attributes) for your different fish; and even then there are simpler solutions.
For what's being asked, I think you really only need one Fish class. When the user defines a new one, what he's really defining are the attribute values.
If you really want new and dynamic attributes, then you could go a long way using e.g. a HashMap to store name/value pairs. You could let the user add "legs" / "4" and then print out that new attribute as-is; but you couldn't make the fish walk on those legs because you'd be missing coding to work with the new attribute.
Have a look at the type object pattern. Also google for it I just gave one of the first references I found...
You may also look the Reflection pattern...
Let the user define attribute values of an instance of, say, a FishSpecies class, and give the FishSpecies a method createFish that creates a fish of that species (i.e. having those attribute values). Keeping track of all FishSpecies objects in a list grants you the opportunity to manage FishSpecies objects, and create Fish objects of given species.
If I understand your question correctly, then I believe that complicating things more than this is a mistake.