Multiple object types for varargs in a method prototype?

Viewed 11070

I'm trying to write the prototype of a Java function that can be called with any number of integers and strings:

myMethod(1, 2, 3, "Hello", "World"); // Valid call
myMethod(4, "foo", "bar", "foobar"); // Valid call

Ideally, I would like the ints and strings to be given in any order (and possibly mixed):

myMethod(1, "Hello", 2, "World", 3); // Valid call

I thought of using varargs, but there can be only one in the prototype. Another idea I've had is to use the following prototype:

public void myMethod(Object ... objs) { [...] }

...but I feel that there should be a compilation error in case it is called with something other than the expected types. Of course, a runtime check (instanceof) could be performed, but that wouldn't be a very elegant solution, would it?

How would you do it?

7 Answers

You can achieve the compile time safety you are after if you can accept some upper limit n on the maximum number of arguments.

The lambda-factory project faced a somewhat similar problem and in that project the problem was solved by autogenerating a class with a large number of overloaded methods - one for each combination.

In your case you would need to autogenerate (upon compilation of your library) a class with a large number of overloaded methods, each accepting some combination of String and Integer arguments.

From a user perspective one could then simply call:

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

and take pleasure in knowing that if it compiles, it works. Of course, the IDEs method completion popup will contain quite a bit of overloaded method suggestions, but as long as the general behavior is documented in your Librarys javadoc, I think this is acceptable.

How to do it:

  1. Create a marker annotation a la the GenerateLambdaMarkerClass annotation from aforementioned Github project. Here you can fx specify the number of maximum allowed number of arguments.
  2. Create an AbstractProcessor a la GenerateLambdaProcessor from aforementioned project. This class will be invoked upon compilation of your library and autogenerate the class with the large number of overloaded methods.
  3. Add a section to your pom, that ensures that the compilation process first compiles your AbstractProcessor and any dependencies (since round 2 of the compilation needs this) and then compiles the rest of your project which can now references the autogenerated class. See the maven-compiler-plugin configuration in aforementioned project's pom file (pom.xml).

Here is a copy paste of the pom plugin that handles step 3 above:

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