Java 不确定类型参数的数量不确定

Java indeterminate number of arguments of indeterminate type

提问人:bitmous 提问时间:10/18/2016 最后编辑:bitmous 更新时间:10/18/2016 访问量:696

问:

我的公司有一个应用程序服务器,它以自己定制的 XTML 语法接收指令集。由于这是有限的,所以有一个特殊的“drop to Java”命令将参数发送到 JVM (1.6.0_39)。参数仅作为“in”或“in/out”传递,其中特殊的“in/out”变量是用于此平台的可变变量库。

以前,接收外部配置的唯一方法是使用不同的特殊命令从 XTML 文件中读取。由于不值得深入研究的原因,这种配置方法很难扩展,所以我正在研究一种使用 Java 的方法。

此配置的语法是 (String,T) 的两个元组,其中 String 是 XTML 文件中的属性名称,T 是应用程序服务器将为其分配属性值的 in/out 可变对象。

我正在尝试使这种转换尽可能无缝,而不必在应用程序服务器中进行烦人的字符串解析。

我已经有一个函数了

public String[] get(String ... keys)

从应用程序服务器的键中检索值,但我真正需要的是一个函数

public static void get(T ... args)

接受两个元组。但是,请注意,它必须是静态的才能从应用程序服务器调用,我的理解是 T 不能在静态上下文中使用。

我不知道如何以不需要(至少)两个步骤的方式处理这个问题,并且没有办法遍历应用程序服务器中的参数。

我知道我在这里工作有一系列严格的限制,所以如果答案是“你必须做一些搞砸的东西”,那很好——我只是想以另一种方式深入了解。

--编辑--

编辑更具体的示例。

配置是一组键值对,可以位于数据库或文件中。get 函数为:

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;

如果我有我的方式,它看起来像......

// 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);
   }
}
爪哇岛 可变 类型名称

评论

0赞 chrylis -cautiouslyoptimistic- 10/18/2016
你可以在静态上下文中使用,但你必须以某种方式将其绑定到静态函数,并且不清楚从哪里获取任何信息。调用代码外观的简短示例可能会有所帮助。Tget
0赞 bitmous 10/18/2016
谢谢,@chrylis。我只是编辑了一些更具体的代码。
1赞 nbrooks 10/18/2016
@bitmous 很明显,你已经在这方面做了一些工作,额外的细节可能很有用,但通常最好将你的问题减少到理解你的问题并提供解决方案所需的最少部分。通读不相关的代码和应用程序的完整背景故事可能会使磨练问题并提供有意义的响应变得更加困难。人们也不太可能阅读它。

答:

3赞 nbrooks 10/18/2016 #1

可以在静态上下文中使用泛型类型。您的问题对于您打算如何执行此操作有些模糊/不清楚,但请考虑以下示例:

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)对方法进行静态引用,并使用泛型类型进行参数化。printNamesType

请注意,这是类型安全的。尝试将不同类型的对象传递到该参数化方法中将在编译时失败(假设此时已知类型不同):

 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...)

编辑

根据您的评论,问题不在于您正在尝试为方法参数使用泛型类型(无论如何,在泛型一词的 Java 意义上);您只是在查找任何非特定的父类,这些父类和您的自定义类型都从中继承。只有一个这样的类:.StringObject

如果您有任何灵活性,我强烈建议您重新考虑您的设计,因为这将导致糟糕的 API 设计。但是,可以使用 .Object... objs

例如:

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

评论

0赞 bitmous 10/18/2016
感谢您的回复,以及您在上面的评论。问题恰恰出在你的编辑上——它需要接受可变数量的字符串和“可变”(一组已知的类)。我想没有办法让 Type() 强制执行已知类型吗?
1赞 nbrooks 10/18/2016
@bitmous Java 是非常严格的类型,因此您的方法签名必须定义它期望接收的参数(如果它们属于不同类型的参数)。Varargs 参数 () 只能在方法 (Docs的最终参数位置使用,因此不能同时使用长度可变的参数和其他类型。我会用另一种方式进行更新。Param...String
0赞 bitmous 10/18/2016
谢谢@nbrooks!我现在有一个解决方案。请参阅我发布的答案。:)
0赞 bitmous 10/18/2016 #2

根据@nbrooks工作,我找到了解决方案。我制作了一个临时的 MutableString(由库提供的类替换)。

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