Why can't we just use arrays instead of varargs? - java

I just came across varargs while learning android(doInBackground(Type... params)) ,SO posts clarified the use of it
My question is why can't we just use Arrays instead of varargs
public void foo(String...strings) { }
I can replace this type of a call by packing my variable number of arguments in an array and passing it to a method such as this
public void foo(String[] alternativeWay){ }
Also does main(String[] args) in java use varargs , if not how are we able to pass runtime parameters to it
Please suggest the benefits or use of varargs and is there there anything else important to know about varargs

The only difference between
foo(String... strings)
and
foo(String[] strings)
is for the calling code. Consider this call:
foo("a", "b");
That's valid with the first declaration of foo, and the compiler will emit code to create an array containing references to "a" and "b" at execution time. It's not valid with the second declaration of foo though, because that doesn't use varargs.
In either case, it's fine for the caller to explicitly create the array:
for(new String[] { "a", "b" }); // Valid for either declaration
Also does main(String[] args) in java use varargs , if not how are we able to pass runtime parameters to it
When it's written as main(String[] args) it doesn't; if you write main(String... args) then it does. It's irrelevant to how the JVM treats it though, because the JVM initialization creates an array with the command line arguments. It would only make a difference if you were writing your own code to invoke main explicitly.

We could use arrays instead of varargs. Varargs are syntactic sugar for using arrays. But they make your code more compact and more readable. Compare
private void foo(String... ss) { ... }
private void bar() {
...
foo("One", "Two", "Three");
...
}
with
private void foo(String[] ss) { ... }
private bar() {
...
foo(new String[] { "One", "Two", "Three" });
...
}
Similarly, we don't need the diamond operator (<>, Java 7) or lambdas (Java 8) either. But they do make code more readable and therefore more maintainable.

One advantage of varargs is for methods requiring at least one parameter, such as max. With varargs you can do it like this
static int max(int first, int... remaining) {
int max = first;
for (int number : remaining)
max = Math.max(max, number);
return max;
}
This is great, because it is impossible to pass no parameters to the max method, and the calling code for max is really clean: max(2, 4, 1, 8, 9). Without varargs the only way to have enforced the condition that at least one number should be passed would have been to have thrown an exception at runtime if the array had length 0 (always best avoided) or to force the caller to write max(2, new int[] {4, 1, 8, 9}) which is really ugly.

Because you function call looks more like a function call, ex.:
new MyAsyncTask().execute("str1", "str2");
looks better than:
new MyAsyncTask().execute(new String[]{"str1", "str2"});
There is no magic behind AsyncTask, very often you dont really need to pass any parameters, sometimes you pass parameters to constructor instead of execute. There are also implementations of AsyncTask :
https://github.com/roboguice/roboguice/blob/master/roboguice/src/main/java/roboguice/util/SafeAsyncTask.java
that dont use varargs at all

Related

Varargs and Generics in java

Consider the following scenario:
<T> void function(T...args){
...code...
}
And then I call it using a Integer[]. How does the compiler assume that T is an Integer, and not a Integer[]? (Note, I'm glad that this is the case, but I still find the ambiguity odd).
Furthermore, if I wanted T to be Integer[], is there anyway for me to do that (assuming boxing/unboxing doesn't exist)?
The Java compiler is smart enough to know that, since you gave it an Integer[], you probably meant for T to be Integer, not Integer[]. I'd assume this is part of the Java language specification that defines ... as varargs.
If you want to specify what T is, you can do that with the following syntax:
Integer[] ary = { 1, 2, 3 };
myObj.function(ary); // T is Integer
myObj.<Integer>function(ary); // T is Integer
myObj.<Integer[]>function(ary); // T is Integer[]
<Integer>function(ary); // this is invalid; instead you could do...
this.<Integer>function(ary); // this if it's an instance method
MyClass.<Integer>function(ary); // or this if it's static
Generics works on object references, so <T> will work on object references of a class. int[] is a class that references an array of int, while int is a primitive. Integer[] is a class that references to an array of Integer, where Integer is another class.
After reviewing this, the varargs param T ... args expects an array of object references, so int[] would be a single element in the array of object references, while Integer[] is an array of object references.
If you want to send an Integer[] as each element of your varargs, you can send an Integer[][]. I wrote an example:
public class SomeMain {
static <T> void foo(T...ts) {
for(T t : ts) {
System.out.println(t);
}
System.out.println();
}
public static void main(String[] args) {
int[] ints = { 1, 2, 3 };
Integer[] integers = { 1, 2, 3 };
foo(ints);
foo(integers);
//note, here each element in the varags will behave as Integer[]
foo(new Integer[][] { integers });
}
}
Output (the hash code of the array will change on every run):
[I#8dc8569
1
2
3
[Ljava.lang.Integer;#45bab50a
There are 3 phases in finding the applicable methods. On the 1st phase, javac tries to match argument types and method parameter types exactly. The parameter type of the method is T[] on this phase, the argument type is Integer[], the two matche after T is inferred to be Integer, therefore the method is chosen as the applicable method (there are no other overloading methods to consider). No further phases are carried out.
If the 1st phase does not yield an applicable method, javac will continue to other phases. For example, if T is explicitly specified as Integer[], the method will not match on the 1st phase (because T[] would not match Integer[])
On the 3rd phase, varargs are considered; javac will match T, not T[], with trailing argument types.
This is indeed, quite confusing, and appear to be ambiguous to our intuition.
Note that Generics isn't completely relevant to the question. The exact same question would apply if the function signature were void function(Object... args) -- if you pass an expression of type Integer[], it could interpreted as either using the array as args, or as one of the elements of args.
The answer is that, basically, the compiler will prefer to use the argument as args if possible. Since the expression you are passing has "array of reference type" type, it is compatible with args, and therefore, that interpretation prevails.
Furthermore, if I wanted T to be Integer[], is there anyway for me to
do that (assuming boxing/unboxing doesn't exist)?
Since it is a generic method, you can explicitly specify the the type argument when calling: this.<Integer[]>function(...).
But back to the more general question where the function signature is void function(Object... args). You could explicitly create the array of arguments yourself:
function(new Integer[][]{ myIntegerArray });
or (simpler) you can cast the expression to a type that is no longer an array of reference type:
function((Object)myIntegerArray);

Define a String array in Java

In java we can define main() method as both these ways.
public static void main(String[] args) {
System.out.println("Hello World");
}
.
public static void main(String... args) {
System.out.println("Hello World");
}
Both method takes array of String arguments. Now consider following scenario.
String[] arr=new String[10]; // valid
String... arr=new String[10];// invalid
Java never allows to create an array like this wayString... arr=new String[10];. But in above method implementation java allows to do so. My question is how java achieve this two different behavior for two scenarios?
...
is a syntax for method arguments and not for variable definitions. The name of this notation is varargs, which is self explanatory i.e variable number of arguments.
Variable argument or varargs(...) in Java used to write more flexible methods which can accept as many argument as you need not for initialization.
... refers to varargs and its main intention to make method more readable.
void method(String... args) {
}
can be called as
method("a"); OR method("a", "b"); OR method("a", "b", "c");
I see no point in using it in variable declaration, we can't do much with
String... a = {"a", "b"}
An array can anyways be declared with dynamic size
String[] arr = {"a"};
OR
String[] arr = {"a", "b"};
You can use varargs in main because a method declared with varargs (...) is bytecode compatible with a declaration of a method with an array argument (for backwards compatibility). That does not mean that the same syntax is allowed for type declarations.

what is the difference between main(String... s) and main(String[] s)?

class Test{
public static void main(String... s){
System.out.println("Hello");
}
}
class Test{
public static void main(String[] s){
System.out.println("Hello");
}
}
What is the difference between above two syntax of main() declaration?
Does Java has any special need to have variable length argument?
No difference (when you run the program from the command line, i.e. what the main method is used for). The first variant appeared after Java 5 introduced varargs.
In short, varargs allows you to pass a variable number of arguments to a method. For the method body the arguments are grouped into an array. Like Test.main("foo", "bar") instead of Test.main(new String[] {"foo", "bar"}). The compiler does the array creation for you behind the scene.
The only difference is if you call main directly from other Java code. The first form allows you to write:
Test.main("first", "second", "third");
whereas the second would require you to create an array explicitly:
Test.main(new String[] { "first", "second", "third" });
Personally I don't think I've ever seen the first form used - calling main from other code is pretty rare. There's nothing wrong with it though.
There is no difference.
In general, String... s allows to pass arguments with comma as separator, while the String[] s requires an array.
But in the implementation s is array in both cases. So ... is sintactic sugar in a sense.
Variable number of arguments main(String... s) was only introduced in Java 5.0.

Java unpacking argument lists

Here's another question of "How would I do this in Java?" In Python, I can use the '*' symbol to unpack arguments like so:
>>> range(3, 6) # normal call with separate arguments
[3, 4, 5]
>>> args = [3, 6]
>>> range(*args) # call with arguments unpacked from a list
[3, 4, 5]
Java supports getting a list of args with ...args syntax, but is there a way (perhaps using the Reflection libraries?) to unpack those for some other function?
public void printStrings(String... strings)
{
// the strings parameter is really a String[].
// You could do anything to it that you normally
// do with an array.
for(String s : strings){
System.out.println(s);
}
}
Can be called like this:
String[] stringArray = new String[10];
for(int i=0; i < stringArray.length; i++){
stringArray[i] = "String number " + (i+1);
}
printStrings(stringArray);
The ... syntax is really syntactic sugar for arrays.
Java doesn't have the facility that you describe, but you could fake it several ways.
I think the closest approximation means overloading any function that you want to use in that fashion using varargs.
If you have some method:
public void foo(int a, String b, Widget c) { ... }
You can overload it:
public void foo(Object... args) {
foo((Integer)args[0], (String)args[1], (Widget)args[2]);
}
But this is really clumsy and error prone and hard to maintain.
More generically, you could use reflection to call any method using any arguments, but it's got a ton of pitfalls, too. Here's a buggy, incomplete example of how it gets ugly really fast:
public void call(Object targetInstance, String methodName, Object... args) {
Class<?>[] pTypes = new Class<?>[args.length];
for(int i=0; i < args.length; i++) {
pTypes[i] = args[i].getClass();
}
Method targetMethod = targetInstance.getClass()
.getMethod(methodName, pTypes);
targetMethod.invoke(targetInstance, args);
}
If the function you're calling is not a varargs function (declared with ...) then you do need to use reflection. Method.invoke() takes an array Object[] of arguments.
The tough part via reflection is finding the right method (well, it's easy if there's only one method with the same name, otherwise it's very difficult).
The cost is the extra time to lookup/invoke the method.
Of course, if you know the specific method at compile-time, then you can deal with this manually, e.g. here for a 3-argument function:
Object[] args = /* get args from somewhere */
callSomeNonVarargsFunction((Cast1)args[0], (Cast1)args[1], (Cast1)args[2]);

Java array initialization within argument list

How come the first call to someMethod doesn't compile without being explicit that it's String[]?
It's fine to use an array initializer to create a String[] array but you can't use it to pass an argument. Are the curly braces used in some other fashion for passing arguments that derails how I'd expect this to behave?
public void someMethod(String[] arr){
//do some magic
}
public void makeSomeMagic(){
String[] arr = {"cat", "fish", "cow"};
//Does not compile!
someMethod({"cat", "fish", "cow"});
//This compiles!
someMethod(new String[]{"cat", "fish", "cow"});
//This compiles!
someMethod(arr);
}
The compiler error is the following:
The method someMethod(String[]) in the type Moo is not applicable for the arguments (String, String, String)
You can only use the { "hello", "world" } initialization notation when declaring an array variable or in an array creation expression such as new String[] { ... }.
See Section 10.6 Array Initializers in the Java Language Specification:
An array initializer may be specified in a declaration, or as part of an array creation expression (§15.10), creating an array and providing some initial values
If you don't want to use explicit String[], use:
public void someMethod(String... arr){
//do some magic
}
…
someMethod("cm", "applicant", "lead");
The three periods after the final parameter's type indicate that the final argument may be passed as an array or as a sequence of arguments.
Read more.
Or you can use varargs:
public void someMethod(String... arr){
//do some magic
}
public void makeSomeMagic(){
someMethod("cat", "fish", "cow");
}
It's basically a fancy syntax for an array parameter (vararg must be the last parameter in method signature).
You can use the curly braces to initialize an array. In every else case it is used to define blocks of statments.

Categories