I have data type which contains 100 properties and 100 getter methods (getproperty1....getproperty100).
I get an input from the user like
Property1
Property2
.
.
Property100
How can I invoke in a quick way the method in this logic
For property1 I need to invoke getproperty1
For propertyI I need to invoke getpropertyI
How can I do this with out using if else, or switch statement or reflection in an efficient way.
Thanks
Your best bet is probably going to be an array or hashmap of some type, and access it by index/key:
public class DataType {
private Map<String, DataProperty> data = new HashMap<String, DataProperty>();
public DataProperty getProperty(String key) {
return data.get(key);
}
public void setProperty(String key, DataProperty value) {
data.put(key, value);
}
}
Although, 100 properties seems like a lot... see if you should break it up or otherwise re-organize it.
You could refactor the class to be a Map. If you have a large number of objects like that it seems more along the lines of a map than an object.
Map<String, Object>
1. If you need to invoke multiple methods I would suggest using the Strategy design pattern. In it's simplest form you could try
public interface Command<T> {
public T getProperty();
}
and then create as many implementations as necessary.
2. If you are only interested in the return type and not the actual invokation the Map<String, T> would be a better alternative.
3. If you want to pass around the information in your program a good alternative would be to use the enum approach
public enum Command {
Property1("some value"),
Property2("some other value");
private String str;
public Command(String str) {
this.str = str;
}
public String getVal() {
return str;
}
}
Which can be used like
Command cmd = ...
String value = cmd.getVal();
Related
Is there any data structure (or similar) in Java, where it is possible to set values for variables in an object like this:
myInstanceVariable.set("myVariableName").value("200")
I found out that something similar is available by using reflection or maps, but not with the above syntax.
Is there such a data structure or mechanism, or do I have to implement it by myself?
This sounds like a good case where you can use a Map, probably a HashMap.
See the Map documentation here:
https://docs.oracle.com/javase/8/docs/api/java/util/Map.html
See this question here for information on various collections:
What Java Collection should I use?
A map doesn't have quite the syntax you gave above but it's close. myMap.put("myVariableName", "200"); If it was a Map<String, Integer> you could also do myMap.put("myVariableName", 200) although then every value would need to be an Integer.
You can use a map:
Map<String, String> data = new HashMap();
data.put("myVariableName", "200");
String value = data.get("myVariableName");
System.out.println(value); //shows 200
Or, on your own using reflection:
private class Attribute {
public Object target;
public String varName;
public Class clazz;
public Attribute(String varName, Object target, Class clazz) {
this.varName = varName;
this.target = target;
this.clazz = clazz;
}
public void value(String value) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
//Call setter method
Method method = target.getClass().getMethod("set" + varName, clazz);
method.invoke(target, value);
}
}
Example:
public class Person {
private String name;
public String getName() {
return name;
}
//This method must exist
public void setName(String name) {
this.name = name;
}
public void set(String varName) throws Exception {
return new Attribute(varName, this, Person.class);
}
}
And the main:
public static void main(String[] args) throws Exception {
Person p = new Person();
p.set("Name").value("John"); //Notice the first letter uppercase
}
I don't recommend to use this solution, but it is what you are looking for, so... I recommend you to use a wrapped Map.
To start with, using reflection is often not the best solution. Perhaps you should think about what you really want to achieve and think about if it is another way to do it?
Having said that, I think the closest you can get to the fluent reflection you are refering to is jOOR, which is a lightweight third party fluent reflection API for Java.
In your case, you could set the variable using reflection the following way (provided that you have statically imported the necessary methods):
on(myInstanceVariable).set("myVariableName", 200);
If you want precisely the syntax you've provided then no, there isn't. You may create your own data structure however. It will wrap a Map most probably.
I have some variables in an enum that for some reason have to start with '#', for example:
public enum PR304MainDb {
#MODE,
#USERID,
#ROLES,
#MAX_DOC_COUNT
}
The way I use these variables is to put them in a HashMap...:
Map<String, Object> in = new HashMap<String, Object>();
in.put(PR304MainDb.#MODE.toString(), 5);
...and then use the HashMap as parameters when calling a store procedure (which code I can't change).
Also after the call I read the results and do some comparisons, like:
if (out.getKey().equals(PR304MainDb.#MAX_DOC_COUNT.toString())) {
//DO SOMETHING
}
I know that these names are invalid, but is there any alternative way to accomplish this?
Yes there is a way - don't use the enum constant name (don't use YOUR_CONSTANT.toString() as the name).
Enums, like other classes, can have fields and methods. Some possibilities:
public enum PR304MainDb_Possibility1 {
MODE("#MODE"),
USERID("#USERID"),
ROLES("#ROLES"),
MAX_DOC_COUNT("#MAX_DOC_COUNT");
private PR304MainDb_Possibility1(String text) {
this.text = text;
}
public final String text;
}
public enum PR304MainDb_Possibility2 {
MODE,
USERID,
ROLES,
MAX_DOC_COUNT;
public String getText() {return "#" + name();}
}
You can't use # in the symbol name, but what you can do is over-ride the toString method:
public enum PR304MainDb {
MODE,
USERID,
ROLES,
MAX_DOC_COUNT;
#override
String toString() {
return "#"+super.toString();
}
}
You can also do this not using the toString() method at all by defining a new method for the purpose, but that would involve changing the code that is using the enum.
I am creating a store for user preferences, and there are a fixed number of preferences that users can set values for. The names of the preferences (settings) are stored as an Enum:
public enum UserSettingName {
FOO,
BAR,
ETC
}
What I would like to be able to do is store a value type with the name so that the service will store the user's value with the correct Java type. For example, FOO might be a Long, and BAR might be a String. Up until now, we were storing all values as Strings, and then manually casting the values into the appropriate Java type. This has lead to try/catch blocks everywhere, when it makes more sense to have only one try/catch in the service. I understand that Enums cannot have generic types, so I have been playing around with:
public enum UserSettingName {
FOO(Long.class),
BAR(String.class),
ETC(Baz.class)
private Class type;
private UserSettingName(Class type) {
this.type = type;
}
public Class getType() {
return this.type;
}
}
I have a generic UserSetting object that has public T getSettingValue() and public void setSettingValue(T value) methods that should return and set the value with the correct type. My problem comes from trying to specify that generic type T when I create or retrieve a setting because I can't do something like:
new UserSetting<UserSettingName.FOO.getType()>(UserSettingName.FOO, 123L)
Sorry if this isn't exactly clear, I can try to clarify if it's not understood.
Thanks!
UPDATE
Both the setting name and value are coming in from a Spring MVC REST call:
public ResponseEntity<String> save(#PathVariable Long userId, #PathVariable UserSettingName settingName, #RequestBody String settingValue)
So I used the Enum because Spring casts the incoming data automatically.
Firstly you have to step back and think about what you're trying to achieve, and use a standard pattern or language construct to achieve it.
It's not entirely clear what you're going after here but from your approach it almost certainly looks like you're reinventing something which could be done in a much more straightforward manner in Java. For example, if you really need to know and work with the runtime classes of objects, consider using the reflection API.
On a more practical level - what you're trying to do here isn't possible with generics. Generics are a compile-time language feature - they are useful for avoiding casting everything explicitly from Object and give you type-checking at compilation time. You simply cannot use generics in this way, i.e. setting T as some value UserSettingName.Foo.getType() which is only known at runtime.
Look how it done by netty:
http://netty.io/wiki/new-and-noteworthy.html#type-safe-channeloption
They done it by using typed constants:
http://grepcode.com/file/repo1.maven.org/maven2/io.netty/netty-all/4.0.0.Beta1/io/netty/channel/ChannelOption.java#ChannelOption
EDIT:
public interface ChannelConfig {
...
<T> boolean setOption(ChannelOption<T> option, T value);
...
}
public class ChannelOption<T> ...
public static final ChannelOption<Integer> SO_TIMEOUT =
new ChannelOption<Integer>("SO_TIMEOUT");
...
}
EDIT2: you can transform it like:
class Baz {}
class UserSettingName<T> {
public static final UserSettingName<Baz> ETC = new UserSettingName<Baz>();
}
class UserSetting {
public <T> UserSetting(UserSettingName<T> name, T param) {
}
}
public class Test {
public static void main(String[] args) {
new UserSetting(UserSettingName.ETC, new Baz());
}
}
Enums are not the answer here. If you find yourself repeating code everywhere you could just create a utility class and encapsulate all the try/catch logic there. That would cut down on your code redundancy without majorly impacting your current code.
public class Util
{
public static MyObject getObjectFromString(String s)
{
try
{
return (MyObject)s;
}
catch(Exception e)
{
return null;
}
}
}
Then use as follows:
MyObject myObj = Util.getObjectFromString(string);
So I have a data class that is somewhat laid out as:
class MyData {
String str1,str2,str3;
Boolean bool1,bool2;
}
The attributes are to be populated based upon a String input, something like:
public void populate(String s) {
if(s.contains("somevalue") myData.setStr1("xxx");
if(s.constains("something else") myData.setBool1(true);
else myData.setBool1(false);
}
This is, of course, a pretty horrible way to do things as s.contains are actually some pretty hairy conditions, so instead I defined an interface:
public interface DataFinderInterface {
public String findStringData(final String input);
public Boolean findBooleanData(final String input);
}
Therefore the populate method could be rewritten as:
public void populate(String s) {
myData.setStr1(str1Finder.findStringData(s));
myData.setBool1(bool1Finder.findBooleanData(s);
}
The implementations of this interface either define a findStringData or a findBooleanData, which is quite unsatisfying. The populate method needs to know if we are expecting to use the findStringData method or the findBooleanData method.
Is there a better way to do this? Am I being overly picky, because the populate method needs to know what instance of DataFinderInterface to assign to what field anyway?
A single findData method returning a String should be sufficient: the code that processes Booleans can put a call to Boolean.getBoolean() on top of it:
public interface DataFinderInterface {
public String findData(final String input);
}
...
myData.setBool1(Boolean.getBoolean(bool1Finder.findData(s));
The problem with the above (or ONE of the problems) is that you are always calling setStr1 AND setBool1 and I assume you will be calling all of the others as well.
If you MUST use something like the above pattern you might want to consider having MyData hold AtomicRefernce<String> and AtomicReference<Boolean>. Then have getSettableString and getSettableBoolean methods that returns the appropriate reference or null if no match.
If it is only the interface method signature you are worried about this could be solved using generics. However it does seem a little weird to initialize an object from a string that way. Perhaps if you add more details about what problem you are trying to solve, there might be a better solution.
public interface DataFinder<T> {
public T findData(final String input);
}
DataFinder<String> str1Finder = new ... // a class implementing DataFinder<String>
DataFinder<Boolean> bool1Finder = new ... // a class implementing DataFinder<Boolean>
public void populate(String s) {
myData.setStr1(str1Finder.findData(s));
myData.setBool1(bool1Finder.findData(s);
}
Consider using regular expressions to extract the data you need from the input string. I would leave the MyData class as a simple data container and build a separate class for populating it - for example, a MyDataBuilder. This class could use string matching in order to extract the fields and populate them on the object.
How to create a class that add some stuff to a map and display it.
I was wondering about the best interface considering that all the code usually need to be covered with unit tests and it's a problem to test a method that display data.
This was my first thought:
class MyFirstProgram {
private Map<String, String> map = new HashMap<String,String>();
public int insertData(...) {...}
public void displayData(...) {...}
}
...but it's not possible to test anything about the retrieval and there is this display method... so I thougt this:
class MyFirstProgram {
private Map<String, String> map = new HashMap<String,String>();
public int insertData(...) {...}
private Map<String, String> retrieveData(...) {...}
public int displayData(...) {...call ; return status}
}
In this it's possible to test the private method with reflection but there is still this display method...
Any idea about the design?
The pragmatic way is to allow access to the Map (via a default/package access method with a comment // for unit tests, and access that. Actually, even better, name the method forUnitTestGetMap() to make it extra clear and avoid confusion with the standard getXXX naming convention. I'm usually fine with just calling toString() on the Map and comparing to what it should be, YMMV. If your insertData() is just calling standard methods on HashMap (e.g. put()) there isn't that much you can and should test, as most of the code is Java library code.
Alternatively, in the displayData() method, does it create an alternative representation of the Data? Such as XML, JSON, maybe a JPanel? If so, look at that to see that the contents of the Data are correct. Obviously XML is much easier than a JPanel for that, but you can still do something like checking that the JPanel has 3 children and the first is a Button named "OK"...
You can try creating getter and setter for the Map variable.
class MyFirstProgram {
private Map<String, String> map = new HashMap<String,String>();
public setMap(Map data) {...}
public Map getMap(...) {...}
}
And then wherever you want to print the map value use getMap method.
If you don't want to use the setters and getters we can have any method which takes a Map as input and print it.
public void PrintMap(Map data){print(data)}