bitmous bitmous - 1 month ago 9
Java Question

Java indeterminate number of arguments of indeterminate type

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);
}
}

Answer

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; }
    }
}