方法原型中varargs的多个对象类型?

时间:2011-06-12 13:13:52

标签: java variadic-functions

我正在尝试编写可以使用任意数量的整数和字符串调用的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),但这不是一个非常优雅的解决方案,不是吗?

你会怎么做?

7 个答案:

答案 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允许类型为NumberList的类型(例如,实现多个接口的类型)。

答案 6 :(得分:0)

如果您可以接受最大参数个数的上限n,则可以达到所需的编译时安全性。

lambda-factory项目面临着有点相似的问题,在该项目中,该问题是通过自动生成带有大量重载方法的类(每种组合一个)来解决的。

在您的情况下,您需要自动生成(在库编译时),该类具有大量重载方法,每个方法都接受String和Integer参数的某种组合。

从用户角度来看,然后可以简单地调用:

MyClass.myMethod( /*some combination of String and int arguments */)

,并高兴地知道它可以编译,它可以工作。当然,IDE方法完成弹出窗口将包含很多重载的方法建议,但是只要您的Librarys javadoc中记录了一般行为,我认为这是可以接受的。

操作方法:

  1. 在上述Github项目的GenerateLambdaMarkerClass注释中创建标记注释。在这里,您可以fx指定允许的最大参数数量。
  2. 从上述项目中创建一个GenerateLambdaProcessor的AbstractProcessor。编译您的库时将调用该类,并使用大量重载方法自动生成该类。
  3. 在pom中添加一个部分,以确保编译过程首先编译您的AbstractProcessor及其任何依赖项(因为第二轮编译需要此依赖项),然后编译您的项目的其余部分,现在可以引用自动生成的类。请参阅上述项目的pom文件(pom.xml)中的maven-compiler-plugin配置。

以下是处理上述第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>