Java两种方法中的varargs
有没有办法在java中创建一个方法,期待两个不同的varargs? 我知道,由于编译器不知道从哪里开始或结束,因此使用相同的对象类型是不可能的。 但为什么它也不可能与不同的对象类型?
例如:
public void doSomething(String... s, int... i){ //... //... }
有没有办法创建这样的方法? 谢谢!
只有一个vararg,对不起。 但使用asList()使它几乎同样方便:
public void myMethod(List args1, List args2) { ... } ----------- import static java.util.Arrays.asList; myMethod(asList(1,2,3), asList(4,5,6));
在Java中,只允许一个varargs
参数,它必须是签名的最后一个参数。
但无论如何它只是将它转换为数组,所以你应该让你的两个参数显式数组:
public void doSomething(String[] s, int[] i){
一种可能的API设计,其中调用代码看起来像
doSomething("a", "b").with(1,2);
通过“流畅”的API
public Intermediary doSomething(String... strings) { return new Intermediary(strings); } class Intermediary { ... public void with(int... ints) { reallyDoSomething(strings, ints); } } void reallyDoSomething(String[] strings, int[] ints) { ... }
如果程序员忘了with(...)
打电话会有危险
doSomething("a", "b"); // nothing is done
也许这会好一点
with("a", "b").and(1, 2).doSomething();
只允许一个vararg
。 这是因为多个vararg
参数不明确。 例如,如果你传入两个相同类的varargs怎么办?
public void doSomething(String...args1, String...args2);
args1 end和args2从哪里开始? 或者这里有些令人困惑的事情。
class SuperClass{} class ChildClass extends SuperClass{} public void doSomething(SuperClass...args1, ChildClass...args2);
ChildClass扩展了SuperClass,因此可以合法地存在于args1或args2中。 这种混淆是为什么只允许一个varargs
。
varargs
也必须出现在方法声明的末尾。
只需将特定类型声明为2个数组。
虽然这种事情偶尔会有用,但通常如果你发现你在Java中受到限制,你可能会重新设计一些东西并且会更好。 以下是一些可能的其他方式来看待它……
如果这两个列表完全相关,那么您可能希望为两个不同的列表创建一个包装类并传入包装器。 集合周围的包装几乎总是一个好主意 – 它们为您提供了添加与集合相关的代码的位置。
如果这是一种初始化数据的方法,请从字符串中解析它。 例如,“abc,123:def,456:jhi,789”几乎非常容易分割出2个分割语句和一个循环(2-3行代码)。 您甚至可以创建一个小的自定义解析器类,将类似的字符串解析为您提供给方法的结构。
嗯 – 诚实地说,从初始化数据来看,我甚至都不知道为什么你想要这样做,任何其他情况,我希望你会传递2个集合,根本不会对varags感兴趣。
您可以执行此类操作,然后可以在该方法中强制转换和添加其他逻辑。
public void doSomething(Object... stringOrIntValues) { ... ... }
并使用这样的方法:
doSomething(stringValue1, stringValue2, intValue1, intValue2, intValue3);
这是一个老线程,但我认为无论如何这都会有所帮助。
我找到的解决方案不是很整洁,但它的工作原理。 我创建了一个单独的类来处理繁重的工作。 它只有我需要的两个变量和它们的吸气剂。 构造函数自己处理set方法。
我需要传递方向对象和相应的Data对象。 这也解决了数据对不均匀的可能问题,但这可能仅限于我的使用需求。
public class DataDirectionPair{ Data dat; Directions dir; public DataDirectionPair(Data dat, Directions dir) { super(); this.dat = dat; this.dir = dir; } /** * @return the node */ public Node getNode() { return node; } /** * @return the direction */ public Directions getDir() { return dir; } }
然后我会将此类作为方法的vararg传递
public void method(DataDirectionPair... ndPair){ for(DataDirectionPair temp : ndPair){ this.node = temp.getNode(); this.direction = temp.getDir(); //or use it however you want } }
这是不可能的,因为Java语言规范是这样说的(见8.4.1。forms参数 ):
方法或构造函数的最后一个forms参数是特殊的:它可以是一个变量arity参数 ,由类型后面的省略号表示。
请注意,省略号(…)本身就是一个标记(§3.11)。 可以在它和类型之间放置空格,但不鼓励这样做。
如果最后一个forms参数是变量arity参数,则该方法是变量arity方法 。 否则,它是一种固定的arity方法 。
至于为什么只有一个而且只有最后一个参数,这可能是猜测,但可能是因为允许这可能导致不可判定或模糊的问题(例如考虑method(String... strings, Object... objects)
会发生什么method(String... strings, Object... objects)
) ,并且只允许非交叉类型会导致复杂化(例如,考虑以前非交叉类型突然发生的重构),当它是否起作用时缺乏清晰度,以及编译器决定何时适用的复杂性。
我刚刚阅读了关于这个“模式”的另一个问题,但它已被删除,所以我想提出一个不同的方法来解决这个问题,因为我没有在这里看到这个解决方案。
相反,强制开发人员将输入参数包装在List或Array上,使用“curry”方法或更好的构建器模式将非常有用。
请考虑以下代码:
/** * Just a trivial implementation */ public class JavaWithCurry { private List numbers = new ArrayList (); private List strings = new ArrayList (); public JavaWithCurry doSomething(int n) { numbers.add(n); return this; } public JavaWithCurry doSomething(String s) { strings.add(s); return this; } public void result() { int sum = -1; for (int n : numbers) { sum += n; } StringBuilder out = new StringBuilder(); for (String s : strings) { out.append(s).append(" "); } System.out.println(out.toString() + sum); } public static void main(String[] args) { JavaWithCurry jwc = new JavaWithCurry(); jwc.doSomething(1) .doSomething(2) .doSomething(3) .doSomething(4) .doSomething(5) .doSomething("a") .doSomething("b") .doSomething("c") .result(); } }
正如您可以通过这种方式看到的那样,您可以根据需要添加所需类型的新元素。
所有的实现都被包装好了。
如果你不打算在第一个参数的大部分时间内传递大量的字符串,你可以提供一堆重载,这些重载使用不同数量的字符串并将它们包装在一个数组中,然后再调用一个方法将数组作为第一个论点。
public void doSomething(int... i){ doSomething(new String[0], i); } public void doSomething(String s, int... i){ doSomething(new String[]{ s }, i); } public void doSomething(String s1, String s2, int... i){ doSomething(new String[]{ s1, s2 }, i); } public void doSomething(String s1, String s2, String s3, int... i){ doSomething(new String[]{ s1, s2, s3 }, i); } public void doSomething(String[] s, int... i) { // ... // ... }
关于Lemuel Adane(由于缺乏代表而无法对post发表评论:))
如果你使用
public void f(Object... args){}
然后你可以循环使用如何确定一个对象的类(在Java中)?
比如说
{ int i = 0; while(i< args.length && args[i] instanceof String){ System.out.println((String) args[i]); i++ ; } int sum = 0; while(i< args.length){ sum += (int) args[i]; i++ ; } System.out.println(sum); }
或者你打算做的任何事情。
您可以将varargs转换为数组
public void doSomething(String[] s, int[] i) { ... }
然后使用一些帮助方法将varargs转换为数组,如下所示:
public static int[] intsAsArray(int... ints) { return ints; } public static T[] asArray(T... ts) { return ts; }
然后,您可以使用这些辅助方法来转换您的变量参数。
doSomething(asArray("a", "b", "c", "d"), intsAsArray(1, 2, 3));