I could workaround this problem but I cannot understand it, so I am asking for some explanation (and maybe a better question title as well).
Please consider this:
public class TBGService {
// TBGObject is an abstract base class which is extended by model classes
public <T> T doGet(TBGObject model) throws TBGServiceException {
String uri = model.buildUrl(repository) + model.getObjectKey();
GetMethod method = new GetMethod(uri);
T returned = execute(method, credentials, model.getClass());
return returned;
}
}
and this:
public enum TBGTaskAttributes {
private TBGTaskAttributes(String id, String type, String label, Object... flags) {
builder = new TaskAttributeBuilder();
builder.withId(id).withLabel(label);
for (Object flag : flags) {
processFlag(flag);
}
}
public abstract String getValueFromIssue(TBGIssue issue);
public abstract void setValueInIssue(TBGIssue issue, String value);
}
when I write this code to define an enum item:
PROJECT(TaskAttribute.PRODUCT, TaskAttribute.TYPE_SINGLE_SELECT, "Project", new OptionProvider() {
#Override
public Set<Entry<String, String>> getOptions(TaskRepository repository) {
try {
List<TBGProject> list = TBGService.get(repository)
.doGet(new TBGProjects()).getProjects();
[...]
return map.entrySet();
} catch (TBGServiceException e) { [...] }
return null;
}
}) {
#Override
public String getValueFromIssue(TBGIssue issue) {
return issue.getProjectKey();
}
#Override
public void setValueInIssue(TBGIssue issue, String value) {
issue.setProjectKey(value);
}
},
[... other items ...]
I get compiler error (also eclipse auto-completion does not work):
The method getProjects() is undefined for the type Object
and if I hover the doGet method, eclipse show it as defined like:
<Object> Object TBGService.doGet(TBGObject model)
Elsewhere, hovering shows the signature correctly as:
<TBGProjects> TBGProjects TBGService.doGet(TBGObject model)
when called with parameter new TBGProjects().
Just changing:
List<TBGProject> list = TBGService.get(repository)
.doGet(new TBGProjects()).getProjects();
with:
TBGProjects projects = TBGService.get(repository).doGet(new TBGProjects());
List<TBGProject> = projects.getProjects();
makes it work. But what's happening here? What am I missing?
Java infers the type of T based on what you assign the return value of the method to.
If you don't assign the return value to anything, Java has no idea what T should be.
To fix this, you can change the parameter to be of type T so Java can infer T from the parameter you pass:
public <T extends TBGObject> T doGet(T model) throws TBGServiceException {
Related
I've tried the following, and neither work:
public static Comparator<ModelDefects> sortFirstFoundDateAscending() {
return new Comparator<ModelDefects>() {
#Override
public int compare(ModelDefects o1, ModelDefects o2) {
return o1.getFirstFoundDate() - o2.getFirstFoundDate();
}
};
}
public static Comparator<ModelDefects> sortFirstFoundDateAscending() {
return new Comparator<ModelDefects>() {
#Override
public int compare(ModelDefects o1, ModelDefects o2) {
return o1.getFirstFoundDate().compareTo(o2.getFirstFoundDate());
}
};
}
Also tried the < operator via an if statement. These didn't work either. Tonnes of searching online did not reveal the answer to me.
First Found Date and its methods are defined as so:
//Declaration:
private SimpleObjectProperty<Date> firstfounddate;
//Initialisation in Constructor:
this.firstfounddate = new SimpleObjectProperty<>();
//First Found Date
public Object getFirstFoundDate() {
return firstfounddate.get();
}
public void setFirstFoundDate(Date firstFoundDateArg) {
this.firstfounddate.set(firstFoundDateArg);
}
public SimpleObjectProperty<Date> firstFoundDateProperty() {
return firstfounddate;
}
This is for a javafx project.
The key for me here is how to compare an Object with an Object?
You can't compare SimpleObjectProperty when the getter data type is set to object, and the getter shouldn't be set to object anyway.
The SimpleObjectProperty is actually a generic: SimpleObjectProperty<T> with a data type of T. So in my case, changing the data type of the getter from Object to Date and modifying the comparator to read as so: return o1.getFirstFoundDate().compareTo(o2.getFirstFoundDate()) fixed the issue.
I recently asked a question about some code of mine where I used reflection and one who wanted to help me with this problem mentioned that I shouldn't use reflection like this and that there is a better way doing it.
So I have an Interface for searching in external Systems:
public interface ReferenceController {
public Map<String, ReferenceElement> searchElements(String searchField, List<String> searchItems, SystemStage systemStage) throws Exception;
public String getStateMapping(String value);
public Boolean isAvailable(SystemStage systemStage) throws Exception;
}
And I have an ENUM where I declare which external systems I have and how their class is named which uses this interface. So if any other programmer want's to implement a new external system he has only to fill the interface and put two values in this ENUM and tada it should work.
So the part where I used the reflection was
public static void performSingleSearch(ReferenceSystem referenceSystem, String searchField, List<String> searchValues, SystemStage systemStage) throws Exception {
if(!isAvailable(referenceSystem, systemStage)) return;
Map<String, ReferenceElement> result = new HashMap<>();
try {
Class<?> classTemp = Class.forName(referenceSystem.getClassname());
Method method = classTemp.getMethod("searchElements", String.class , List.class, SystemStage.class);
result = (Map<String, ReferenceElement>) method.invoke(classTemp.newInstance(), searchField, searchValues, systemStage);
} catch (Exception e) {
return;
}
if(result != null) orderResults(result, referenceSystem);
}
In the ENUM ther is a function getClassname, which answers with the fqcn.
The Enum looks like this:
public enum ReferenceSystem {
UCMDB (refSystems.ucmdb.UcmdbFunctions.class),
PROIPS (refSystems.proips.ProIPSFunctions.class),
KV (refSystems.kv.KvFunctions.class),
FISERVICE(refSystems.fiservice.FiServiceFunctions.class),
COMMAND (refSystems.command.CommandFunctions.class),
FII (refSystems.fii.FiiFunctions.class);
private Class<?> clazz;
private ReferenceSystem(Class<?> controllerClass) {
this.clazz = controllerClass;
}
public String displayName() {
ResourceBundle bundle = ResourceBundle.getBundle("EnumI18n", Locale.GERMAN);
return bundle.getString(toString());
}
public String localizedDisplayName(Locale locale) {
ResourceBundle bundle = ResourceBundle.getBundle("EnumI18n", locale);
return bundle.getString(toString());
}
public Class<?> getClassname() { return clazz; }
}
I've already altered it according to #jhamon 's answer.
But I get an error when I try
classTemp.newInstance().searchElemets(...)
Because it doesn't know about searchElemts().
So the other user here said there would be the possibility of implementing the interface into the enum and then I don't have to reflect.
Could anyone tell me how, because I don't know and I don't know where or what to search.
Thanks
It seems all your search engines have a common method searchElementsand it's defined in the interface
Knowing that, why not call this method directly, and not by looking for it first. -> no more reflection to find the method.
public interface ReferenceController {
public Map<String, ReferenceElement> searchElements(String searchField, List<String> searchItems, SystemStage systemStage) throws Exception;
public String getStateMapping(String value);
public Boolean isAvailable(SystemStage systemStage) throws Exception;
}
Instead of storing the class name as String in the Enum, store the .class -> no more reflection to find the class.
public static void performSingleSearch(ReferenceSystem referenceSystem, String searchField, List<String> searchValues, SystemStage systemStage) throws Exception {
if(!isAvailable(referenceSystem, systemStage)) return;
Map<String, ReferenceElement> result = new HashMap<>();
try {
Class<?> classTemp = referenceSystem.getClazz();
result = ((ReferenceController) classTemp.newInstance()).searchElements(searchField, searchValues, systemStage);
} catch (Exception e) {
return;
}
if(result != null) orderResults(result, referenceSystem);
}
My company has an application server that receives sets of instructions in their own bespoke XTML syntax. As this is limited, there's a special "drop to Java" command that sends arguments to a JVM (1.6.0_39). Arguments are passed as "in" only, or "in/out", where the special "in/out" variables are a library of mutables for use with this platform.
Previously the only way to receive external configuration was to use a different special command to read from an XTML file. For reasons not worth delving into, this method of configuration is difficult to scale, so I'm working on a way to do this with Java.
The syntax for this configuration was two-tuples of (String,T) where String was the property name in the XTML file, and T was the in/out mutable that the application server would assign the property value to.
I'm attempting to make this transition as seamless as possible, and not have to do annoying string parsing in the application server.
I already have a function
public String[] get(String ... keys)
That retrieves the values from the application servers' keys, but What I really need is a function
public static void get(T ... args)
that accepts the two-tuples. However, note it needs to be static in order to be called from the application server, and my understanding is that T can't be used in a static context.
I'm at a loss for how to approach this problem in a way that doesn't require (at least) two steps, and there is no way to loop over the arguments in the application server.
I know I'm working within a tight set of constraints here, so if the answer is "you have to some messed up stuff", that's fine - I'd just like any insight into another way.
-- edit --
Editing a more specific example.
The configuration is a set of key-value pairs, and can be in a database or a file. The get function is:
public JSONObject get(String ... keys) throws ClassNotFoundException, SQLException, KeyNotFoundException, FileNotFoundException, IOException {
JSONObject response = new JSONObject();
if(this.isDatabase) {
for(int i=0;i<keys.length;i++){
PreparedStatement statement = this.prepare("SELECT value FROM "+this.databaseSchema+"."+this.settingsTableName+" WHERE key = ? LIMIT 1");
statement.setString(1, keys[i]);
ResultSet results = statement.executeQuery();
boolean found = false;
while(results.next()){
String value = results.getString("value");
value = value.replace("\"","");
response.put(keys[i], value);
found = true;
}
if(!found){
throw new KeyNotFoundException(keys[i]);
}
}
} else if (this.isFile) {
boolean[] found = new boolean[keys.length];
BufferedReader br = new BufferedReader(new FileReader(this.settingsFile));
String line;
while((line = br.readLine()) != null ){
String key;
String value;
for(int i=0;i<line.length();i++){
if(line.charAt(i) == '='){
key = line.substring(0,i);
value = line.substring(i+1,line.length());
if(indexOfString(keys,key) != -1){
value = value.replace("\"","");
found[indexOfString(keys,key)] = true;
response.put(key,value);
if(allFound(found)==-1){
return response;
}
}
break;
}
}
}
if(allFound(found)!=-1){
throw new KeyNotFoundException(keys[allFound(found)]);
}
}
return response;
If I had my way, it would look like ...
// ConfigurationReader.java
public class ConfigurationReader{
public ConfigurationReader( ... ){}
public static JSONObject get(String key){
// Get the key
}
}
// ConfigurationInterface.java
public static void get(T ... args){
ConfigurationReader cfgReader = new ConfigurationReader( ... );
for(var i=0;i<args.length;i+=2){
in = args[i];
out = args[i+1];
out = cfgReader.get(in);
}
}
You can use generic types in a static context. Your question is somewhat vague/unclear about how you intend to do this, but consider the example below:
public class Example {
public static void main(String[] args) {
Type t1 = new Type("foo");
Type t2 = new Type("bar");
Type t3 = new Type("baz");
Printer.<Type> printNames(t1, t2, t3);
}
public static class Printer {
#SafeVarargs
public static <T extends Type> void printNames(T... objs) {
for (T obj : objs) {
System.out.println(obj);
}
}
}
public static class Type {
private final String name;
public Type(String name) {
this.name = name;
}
#Override
public final String toString() {
return name;
}
}
}
Printer.<Type> printNames(t1, t2, t3) makes a static reference to the printNames method, parameterized with the Type generic type.
Note that this is type-safe. Attempting to pass an object of a different type into that parameterized method will fail at compile-time (assuming the type is known to be different at that point):
Example.java:8: error: method printNames in class Printer cannot be applied to given types;
Printer.<Type> printNames(t1, t2, t3, "test");
^
required: T[]
found: Type,Type,Type,String
reason: varargs mismatch; String cannot be converted to Type
where T is a type-variable:
T extends Type declared in method <T>printNames(T...)
Edit
Based on your comment, the issue isn't that you're trying use a generic type for your method argument (in the Java-sense of the word generic, anyway); you're simply looking for any non-specific, parent class that both String and your custom type inherit from. There's only one such class: Object.
I'd strongly recommend reconsidering your design if you have any flexibility, since this will make for poor API design. However you can have your method accept an arbitrary number of arbitrarily-typed objects using Object... objs.
For example:
public class Example {
public static void main(String[] args) {
Printer.printNames("a", "b", new Type("foo"), new Type("bar"));
}
public static class Printer {
public static void printNames(Object... objs) {
for (Object obj : objs) {
if (obj instanceof String) {
System.out.println(((String) obj).toUpperCase());
}
else if (obj instanceof Type) {
System.out.println(obj);
}
}
}
}
public static class Type {
private final String name;
public Type(String name) { this.name = name; }
public final String toString() { return name; }
}
}
Based on #nbrooks work, I found a solution. I made a temporary MutableString (to be replaced by the classes provided by the library).
public static class MutableString {
public String value;
public MutableString(){}
}
// One for every mutable type
public static void Pair(String key, MutableString mutable, ApplicationConfiguration appConfig) throws Exception{
mutable.value = appConfig.get(key).toString();
}
public static void Retrieve(Object ... args) throws Exception {
ApplicationConfiguration appConfig = new ApplicationConfiguration( ##args## );
for(int i=0;i<args.length;i+=2){
if(args[i+1].getClass().equals(new MutableString().getClass())){
ApplicationConfiguration.Pair( (String) args[i], (MutableString) args[i+1], appConfig);
} // One for every mutable type
}
}
Explanation of the situation:
I want to instanciate an object that can that have basically 2 parameters. A set of 'meta-parameters' and a value. The type of the value is determined by the 'meta-parameters'.
Example:
public class Meta
{
public static final Meta META_FIRST = new Meta(String.class, new byte[] {0x00, 0x01});
public static final Meta META_SECOND = new Meta(Float.class, new byte[] {0x00, 0x02});
public static final Meta META_THIRD = new Meta(Double.class, new byte[] {0x00, 0x03});
private Class<?> type;
private byte[] prelude;
private Meta(Class<?> type, byte[] prelude)
{
this.type = type;
this.prelude = prelude;
}
public Class<?> getType()
{
return this.type;
}
public byte[] getPrelude()
{
return this.prelude;
}
}
public class Record
{
private # value;
private byte[] prelude;
public Record(Meta meta, # value)
{
this.prelude = meta.getPrelude();
}
public void doSomeWork()
{
//Do some work with prelude and value
}
}
Expected usage:
Record recordString = new Record(Meta.META_FIRST, "hello");
Record recordDouble = new Record(Meta.META_THIRD, 12.8);
My doubt yet is how to determine the type of 'value' (actually symbolized by '#').
I think generics or reflexion could solve my problem but I can't figure out how a parameter in the constructor can influence the type of another parameter.
I would like to avoid using the generic notation when instanciating a Record (that's the reason why I putted this 'generic' information in the Meta-class).
Can anyone has an idea how to solve that ? (feel free to suggest an other approach)
Note: it is also acceptable for me to initialize the record value later with a setter.
In order to have it compiling, you have to make the Record class generic (parameterized by the type of the value):
public class Record<T> {
private T value;
public Record(Meta meta, T value) {
//Initialization
}
}
However, I don't see a reason you have a Meta class, since it does nothing but holding the Class type of the value. In order to simplify the hierarchy and to make sure the Meta is compatible with the value type, I would remove the Meta class and keep a Class<T> in Record, which will represent the meta about the value.
public class Record<T> {
private T value;
private Class<T> meta;
public Record(T value, Class<T> meta) {
//Initialization
}
public Class<T> getMeta() {
return meta;
}
}
and will use it like this:
Record recordString = new Record("hello", String.class);
Class<String> recordStringMeta = recordString.getMeta();
Record recordDouble = new Record(12.8, Double.class);
Class<Double> recordDoubleMeta = recordDouble.getMeta();
Update:
Since you don't want to have the Record class generic (which I don't advice you, but ...), you can introduce three constructors there and copy the passed value to an Object member. Unfortunately, this will force you to do casts when extracting the value back:
public class Record {
private Object value;
public Record(Meta meta, String value) { ... }
public Record(Meta meta, Double value) { ... }
public Record(Meta meta, Float value) { ... }
}
I've been struggling with this for a while and have yet to find an answer. As a result, my brain is somewhat muddled, so pardon me if I make a dumb mistake.
I'm trying to implement a typed INI parser, that will parse this kind of file:
[section1]
<int>intkey=0
<float>floatkey=0.0
<str>stringkey=test
[section2]
<float>x=1.0
<float>y=1.0
<float>z=0.0
In doing so, I have a central class named Config, which handles the basic reading and writing operations. One of the methods of Config is called get(String section, String key), which ideally would return a value appropriate for the requested section-key pair, like so:
Config cfg = new Config("test.ini");
cfg.get("section2", "x"); // 1.0, not "1.0" or some Object that technically represents the float
cfg.get("section1", "intkey"); // 0
cfg.get("section1", "strkey"); // "test"
I'm currently using an enum to handle the conversion of the String to various types, with an abstract method overridden by the different types:
enum Type
{
INTEGER ("int") {
public Object parse(String value) {
try
{
return Integer.parseInt(value);
} catch (NumberFormatException e)
{
return null;
}
}
},
FLOAT ("float") {
public Object parse(String value) {
try
{
return Float.parseFloat(value);
} catch (NumberFormatException e)
{
return null;
}
}
},
STRING ("str") {
public Object parse(String value) {
return value;
}
};
public final String name;
Type(String name)
{
this.name = name;
}
private static HashMap<String, Type> configMap = generateConfigMap();
private static HashMap<String, Type> generateConfigMap()
{
HashMap<String, Type> map = new HashMap<String, Type>();
for (Type type : Type.values())
map.put(type.name, type);
return map;
}
public static Type get(String name)
{
return configMap.get(name);
}
abstract public Object parse(String value);
}
Unfortunately, parse(String value) returns an Object, and when passed out of Config, requires a cast or similar, and ideally this would be self-contained.
If I'm going about this completely wrong and there's a more flexible or simple way to code it, please let me know. I'm open to suggestions. Though I would like to know if there's a way to do this. Maybe with generics...?
Note: I know I'm missing imports and the like. That's not why I'm posting here.
Here's the thing. If the code that calls config.get() doesn't know what type to expect, you can't possibly return anything other than Object since the calling code doesn't know what to expect. Of course you'll have to cast.
Now, if you wanted to design Config in a way that the caller did know what type it was asking for, than that becomes a bit easier. The easiest approach then is to do something like this:
public class Config {
public int getInt(String a, String b) {
return ((Integer)get(a, b)).intValue();
}
}
But until the caller knows what to expect, you really gain nothing from avoiding casts.
If you want to return a a type of object depending on what you get you can do this:
public <T extends MyObject> T myMethod(Class<T> type) {
return type.cast(myObj);
}