我正在尝试编写可以使用任意数量的整数和字符串调用的Java函数的原型:
myMethod(1, 2, 3, "Hello", "World"); // Valid call
myMethod(4, "foo", "bar", "foobar"); // Valid call
理想情况下,我希望以任何顺序(并且可能混合)给出整数和字符串:
myMethod(1, "Hello", 2, "World", 3); // Valid call
我想过使用varargs,但原型中只能有一个。我的另一个想法是使用以下原型:
public void myMethod(Object ... objs) { [...] }
...但我觉得如果用预期类型以外的东西调用它应该有一个编译错误。当然,可以执行运行时检查(instanceof
),但这不是一个非常优雅的解决方案,不是吗?
你会怎么做?
答案 0 :(得分:9)
如果您希望它是类型安全的,我会选择:
public myMethod(Thing<?>... thing) { ... }
然后创建你的Thing类:
public interface Thing<T> {
public T value();
}
public class IntThing implements Thing<Integer> {
private final int value;
public IntThing(int value) {
this.value = value;
}
public Integer value() {
return value;
}
}
我会让你想象一下如何编写StringThing。显然,使用比“Thing”更好的名字,但我无法帮助你。
然后你制作两个静态方法:
public static Thing<Integer> thing(int value) {
return new IntThing(value);
}
public static Thing<String> thing(String value) {
return new StringThing(value);
}
然后在调用thing
:
myMethod(thing(1), thing(2), thing(3), thing("Hello"), thing("World"));
凌乱?对。不幸的是,Java没有能力像其他语言一样隐藏这些东西。 Scala隐含的defs会帮助你,但这带来了一大堆其他问题。就个人而言,我会使用instanceof
检查,但这一检查将确保您的代码在编译时是安全的。
答案 1 :(得分:7)
Java编程语言中无法使其工作,因此您可以传递任意数量的字符串和整数,并且当您传递除字符串或整数之外的其他内容时,编译器会给出错误。
答案 2 :(得分:3)
无法使用泛型来匹配两种类型,例如
public <T extends String | Integer> void myMethod(T... objs); // You can't do this
答案 3 :(得分:1)
您所描述的问题的唯一可能解决方案是已经说过的问题,其中函数采用类型为Object
的varargs参数。这是由于Java中的限制,在方法签名中只能有一个vararg,并且它必须是最后一个参数。
如果不了解您打算myMethod
要执行的操作的详细信息,很难说替代优雅的解决方案是什么。
答案 4 :(得分:0)
(解决方法)
添加到您的lib:
package mylib;
public class Lang {
public static <T> T[] varargs (T...ts) {
return ts;
}
}
在你的计划中:
package myprog;
import static mylib.Lang.*;
public class MyProg {
public static void testfunc (Integer[] ints, String[] strs) {
for (int i : ints)
System.out.println(i);
for (String s : strs)
System.out.println(s);
}
public static void main (String[] args) {
testfunc(
varargs(1,2,3),
varargs("Sophia", "Jacob")
);
}
}
(打破许多编码风格规则)
答案 5 :(得分:0)
允许使用语法
public class Foo<T extends Number & List> {
...
}
类型变量T
允许类型为Number
和List
的类型(例如,实现多个接口的类型)。
答案 6 :(得分:0)
如果您可以接受最大参数个数的上限n,则可以达到所需的编译时安全性。
lambda-factory项目面临着有点相似的问题,在该项目中,该问题是通过自动生成带有大量重载方法的类(每种组合一个)来解决的。
在您的情况下,您需要自动生成(在库编译时),该类具有大量重载方法,每个方法都接受String和Integer参数的某种组合。
从用户角度来看,然后可以简单地调用:
MyClass.myMethod( /*some combination of String and int arguments */)
,并高兴地知道它可以编译,它可以工作。当然,IDE方法完成弹出窗口将包含很多重载的方法建议,但是只要您的Librarys javadoc中记录了一般行为,我认为这是可以接受的。
操作方法:
以下是处理上述第3步的pom插件的副本粘贴:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <!-- https://maven.apache.org/plugins/maven-compiler-plugin/compile-mojo.html -->
<version>2.3.2</version>
<executions> <!--https://issues.apache.org/jira/browse/MCOMPILER-97 -->
<execution>
<id>default-compile</id>
<!-- phase and goal is inherited from the super pom for the default-compile (and are compile) -->
<configuration>
<compilerArgument>-proc:none</compilerArgument>
<includes>
<include>com/hervian/lambda/MethodParameter.java</include>
<include>com/hervian/lambda/GenerateLambda.java</include>
<include>com/hervian/lambda/GenerateLambdaProcessor.java</include>
</includes>
</configuration>
</execution>
<execution>
<id>compile-markerclass-and-process-annotation</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
<configuration>
<annotationProcessors>
<annotationProcessor>com.hervian.lambda.GenerateLambdaProcessor</annotationProcessor>
</annotationProcessors>
<!-- <generatedSourcesDirectory>default is ${project.build.directory}/generated-sources/annotations</generatedSourcesDirectory> -->
<includes>
<include>com/hervian/lambda/GenerateLambdaMarkerClass.java</include>
</includes>
</configuration>
</execution>
<execution>
<id>compile-everything-else</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>