Is there an easier alternative to mimicking the splat operator?
Asked Answered
C

3

7

I've found it's available in Ruby, but I recognize it from what I've done in Python; the "splat" operator. Long story short, I'm wondering if there's a simpler way to accomplish what I currently am, mimicking what the "splat" operator does.

I made a central method that the rest can call because I realized I have several very similar ones, and they were all doing the same except for a few minor things. Here's the method signature:

private String callScript(String scriptLocation, String... extraArgs) throws Exception {

I want to require at least one argument (the scriptLocation), and then allow any number of extra arguments. What I end up doing with this is creating a ProcessBuilder. My desire is to do something like this:

ProcessBuilder pb = new ProcessBuilder("something", scriptLocation, /* extraArgs */);

But of course, the ProcessBuilder constructor only accepts things like:

  • List<String>
  • String[]
  • String...

So my approach obviously wouldn't work.

My current workaround, which works fine as far as I know, is:

List<String> finalArgs = new ArrayList<String>();
finalArgs.add("something");
finalArgs.add(scriptLocation);
finalArgs.addAll(Arrays.asList(extraArgs));
ProcessBuilder pb = new ProcessBuilder(finalArgs);

(yes, I understand I don't have to use List and could just make a String[])

(and yes, I understand I could loop through extraArgs and add them to finalArgs individually, instead of having to use addAll and Arrays.asList())

(and yes, I know I can make a function that effectively accomplishes my workaround by returning certain arguments combined with variable arguments)

So I guess outside of these last three statements, is there anything that can achieve this?

Choline answered 27/7, 2013 at 0:28 Comment(6)
String... is actually String[]. If you split your parameters like String s1, String[] s2ToN to enforce the first parameter, creating a copy in a new array / list is the only thing you can do to merge them.Youngs
@Youngs Sorry, I knew that but it seems I'm misunderstanding/overthinking that part. So you're basically saying that my current approach (in some form) is really the only way to handle this? No problem if it is, I just didn't know if I was missing somethingCholine
i'm not sure i understand the question... you're looking for a better way to put some strings in a List?Nitrogen
@Nitrogen I just didn't know if there was another way to "unpack" varargs (like the splat operator), without my approach of combining them into a list/arrayCholine
Yep, there is no other way to unpack / merge varargs since every array[] is final in it's size. If you need to add one element you have to create a bigger array and copy data in there. ArrayList does the same internally. There are ways to make that operation more efficient than you did but that's it. (e.g. System.arraycopy())Youngs
@Youngs Works for me, just wanted to see if there was something obvious I'm missing. If you create an answer with basically this last comment of yours (and maybe include a simple example of applying System.arraycopy() to my example), I'd happily accept it over the other answer (no offense to them).Choline
K
2

It depends on your definition of simpler, but you could write a class utilizing the Builder pattern:

public class MyProcessBuilder {
    List<String> args;

    MyProcessBuilder( String text, String location ) {
        args = new ArrayList<String>();
        args.add( text );
        args.add( location );
    }

    MyProcessBuilder arg( String arg ) {
        args.add( arg );
        return this;
    }

    MyProcessBuilder args( String... args ) {
        this.args.addAll( Arrays.asList( args ) );
        return this;
    }

    public ProcessBuilder go() {
        return new ProcessBuilder( args );
    }

    public static MyProcessBuilder callScript( String location ) {
        return new MyProcessBuilder( "something", location );
    }

    public static void main( String[] args ) {
        callScript( "/tmp/bla" ).arg( "something else" ).arg( "more" ).go();
    }
}
Katsuyama answered 23/8, 2013 at 20:58 Comment(1)
I don't know why I didn't think of something like this, but it's definitely a good suggestion; thanks!Choline
U
0

Write a utility splat() and publish it?:)

This might look more uniform:

List<String> finalArgs = new ArrayList<String>();

finalArgs.addAll(Arrays.asList( "something" ));
finalArgs.addAll(Arrays.asList( scriptLocation ));
finalArgs.addAll(Arrays.asList( extraArgs )));

ProcessBuilder pb = new ProcessBuilder(finalArgs);
Unroll answered 27/7, 2013 at 0:48 Comment(0)
S
0

When you call

ProcessBuilder pb = new ProcessBuilder("something", scriptLocation, /* extraArgs */);

If you have

public ProcessBuilder(String something, String scriptLocation, String... extraArgs);

Then you can call this method in many ways:

ProcessBuilder pb = new ProcessBuilder("something", "/tmp/script.sh");

ProcessBuilder pb = new ProcessBuilder("something", "/tmp/script.sh","someArg");

ProcessBuilder pb = new ProcessBuilder("something", "/tmp/script.sh","someArg","someArg2");



ProcessBuilder pb = new ProcessBuilder("something", "/tmp/script.sh");

ProcessBuilder pb = new ProcessBuilder("something", "/tmp/script.sh",new String[]{});

ProcessBuilder pb = new ProcessBuilder("something", "/tmp/script.sh",new String[]{"someArg"});

ProcessBuilder pb = new ProcessBuilder("something", "/tmp/script.sh",new String[]{"someArg","someArg2"});



ArrayList<String> someStringList = new ArrayList<String>();
someStringList .add("SomeArg");
someStringList .add("SomeArg2");
String[] extraArgs = someStringList .toArray(new String[someStringList .size()]);
ProcessBuilder pb = new ProcessBuilder("something", "/tmp/script.sh",extraArgs);
Stockish answered 27/7, 2013 at 1:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.