We declare String array like-
String[] a={"A"};
But when a method has String array as argument, why can't we call the method like-
mymethod({"A"});
Code-
class A{
static void m1(String[] a) { }
public static void main(String args[]){
m1(new String []{});//OK
m1({}); //Error
}
}
You can, although your syntax is a bit off.
mymethod(new String[]{"A"});
That's just the way the language is specified. From section 10.6 of the JLS:
An array initializer may be specified in a declaration (§8.3, §9.3, §14.4), or as part of an array creation expression (§15.10), to create an array and provide some initial values.
So you've seen it working in a declaration, and an array creation expression is the form which includes new ArrayElementType at the start:
myMethod(new String[] {"A"});
Bear in mind that when it's part of a declaration, there's only one possible element type involved. For method invocations, it's trickier - there could be multiple overloaded methods, etc. Basically, you'd need to make the expression {"A"} evaluate as a string array on its own, before participating in overload resolution.
For a bit of comparison, the same is true in C#, although C# 3 introduced implicitly typed arrays where the element type is inferred from the values, so you'd be able to write:
// C# 3
MyMethod(new[] {"A"});
You still need the new[] part though.
You can't pass an array like that. Declare it as a variable, then pass the variable to the method instead.
Related
I'm learning how to use stream, and I get a problem with this method.
public static String[] inArray(String[] array1, String[] array2) {
return Arrays.stream(array1)
.filter(str -> Arrays.stream(array2).anyMatch(s -> s.contains(str)))
.distinct().sorted().toArray(**String[]::new**);
}
I'm so confused about String[]::new, could you give me a hint?
String[]::new means size -> new String[size].
When Stream#toArray(IntFunction<A[]> generator) is ready to produce an array, it calls generator and passes (generator.apply) the size of the inner collection to get a collection to fill it up.
I would say the existing answers provide some insight but none of them yet talk about IntFunction<R>.
To add to them explain, what it means in the context of Stream.toArray(String[]::new) is that it represents an IntFunction implementation such as :
new IntFunction<String[]>() {
#Override
public String[] apply(int value) {
return new String[value];
}
}
where the code creates a newly allocated String[] of size value and produces the array of that size as an output.
You are right to be confused, because Java isn't really super clear about types vs. classes.
We know that String[] is a type, as you can declare variables of that type:
jshell> String[] s = new String[]{"Hello", "world"}
s ==> String[2] { "Hello", "world" }
However, String[] actually is treated as a class in Java and not just a type:
jshell> s.getClass()
$2 ==> class [Ljava.lang.String;
That funny looking [Ljava.lang.String, representing the type "array of string" shows up in response to the getClass invocation. I agree that it is surprising. But every object in Java has to have a class, and String[] is that class. (In other languages, you might see something like Array<String> which might be a dash clearer. But then Java has type erasure so again, things look a little confusing.)
In your particular case, here's what's going on. You need to be careful with types when making arrays from streams. Naively, you might get:
jshell> Arrays.asList("a", "b").stream().toArray()
$5 ==> Object[2] { "a", "b" }
So we want the version of toArray that gives us an array:
jshell> Arrays.asList("a", "b").stream().toArray((n) -> new String[n])
$7 ==> String[2] { "a", "b" }
That's better! The result type is an array of strings, instead of just an array of obejcts. Now the (n)->new String[n] can be replaced with a method reference for construction. Java allows array types in method references! So we can write:
jshell> Arrays.asList("a", "b").stream().toArray(String[]::new)
$8 ==> String[2] { "a", "b" }
Aside: There are some caveats when using array types in method references like this, such as the requirement that the array type must be reifiable, but I think that's a little beyond what you might have been asking. The TL;DR here is that, by design, Java allows array types in (constructor-like) method references with ::new.
This is a method reference expression see JLS 15.13. The syntax for method references is:
MethodReference:
ExpressionName :: [TypeArguments] Identifier
Primary :: [TypeArguments] Identifier
ReferenceType :: [TypeArguments] Identifier
super :: [TypeArguments] Identifier
TypeName . super :: [TypeArguments] Identifier
ClassType :: [TypeArguments] new
ArrayType :: new
The particular case you are looking at is the last one. In your example, String[] is an ArrayType which means that it consists of a type name followed by one or more [].
There shouldn't be a class named String[] which is very lame and I could not interpret what it is actually meant for.
See above: it is a type specification not a class name. From a syntactic / linguistic perspective, this usage is analogous to:
Class<?> c = String[].class;
or
if (a instanceof String[])
or even
public void myMethod(String[] arg)
(You wouldn't call those "lame" ... would you?)
Now you could have a valid case for saying that it is syntactically unexpected (especially to a pre-Java 8 programmer) to be able to use the new keyword like this. But this unexpected syntax is a consequence of the strong imperative that the designers have to NOT break backwards compatibility when adding new language features to Java. And it is not unintuitive. (At least, I don't think so. When I first saw this construct, is was obvious to me what it meant.)
Now, if they were starting with a clean slate in 2018, a lot of details of the Java language design would be simpler and cleaner. But they don't have the luxury of doing that.
The documentation of Stream#toArray says it exactly:
The generator function takes an integer, which is the size of the desired array, and produces an array of the desired size.
for example:
IntFunction<int[]> factory = int[]::new;
// v--- once `apply(3)` is invoked,it delegates to `new int[3]`
int [] array = factory.apply(3);
// ^--- [0, 0, 0] create an int array with size 3
String[]::new is a method reference expression and it must be assigned/casted to a certain functional interface type at compile time:
A method reference expression is used to refer to the invocation of a method without actually performing the invocation. Certain forms of method reference expression also allow class instance creation (§15.9) or array creation (§15.10) to be treated as if it were a method invocation.
A method reference expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.
Edit
As #Eugene mentioned in comments below. It's necessary to let you know how and where the stream create an fixed size array to collecting all elements.
The following table is showing the stream how to calculates the array size:
sequential stream - AbstractSpinedBuffer#count
parallel stream
stateless OPs with known/fixed size Spliterator - AbstractConcNode#AbstractConcNode
stateful OPs
fixed size Spliterator - Spliterator#estimateSize
unknown size Spliterator - AbstractConcNode#AbstractConcNode
The following table is showing the stream where to creates a fixed size array by array generator IntFunction:
sequential stream
stateful/stateless OPs with unknown/fixed size Spliterator - SpinedBuffer#asArray
parallel stream
stateless OPs with known/fixed size Spliterator - Nodes#flatten
stateful OPs
fixed size Spliterator - Nodes#collect
unknown size Spliterator - Nodes#flatten
String[]::new
This is lambda for the following method:
public String[] create(int size) {
return new String[size];
}
Your whole stream operation is terminating converting that into an array, that is what you do with the last method toArray(), but an array of what?....
of Strings ( thus String[]::new)
The parameter of toArray(...) is a Functional Interface (namely IntFunction<R> and then String[]::new is defined as the Method Reference or in that case constructor to use that generates an array of the desired type.
See https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html
And https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html
Adding to the answer of Andrew Tobilko:
"String[]::new means size -> new String[size]"
which, since toArray takes an IntFunction, is similar to:
IntFunction<String[]> generator = new IntFunction<String[]>() {
#Override
public String[] apply(int size) {
return new String[size];
}
};
To convert your stream to another List, you can use:
.collect(Collectors.toList());
I was writing a method with a variable number of parameters, using the constructor proposed by Java:
public static boolean myMethod(String... strings) {
for (String s: strings) {
// Applying some logic / treatment on strings
}
}
I was wondering: how does Java behaves with the strings parameter ? Does it takes it as an ArrayList, or a simple String[] ? Is the order of the parameters kept while going through the method ?
For example, if I use my method like this:
MyMethodUtils.myMethod(first, second, third, fourth, fifth);
Am I guaranteed that first will be treated first, then second, etc ... Or not ?
Bonus question: What is the specifical name of such constructor String... (in Java) ?
Yes, ordering is preserved.
This is because String... (technically called a variable arity parameter, but often informally called "varargs") is just syntactic sugar for String[]. So:
MyMethodUtils.myMethod(first, second, third, fourth, fifth);
is identical to
MyMethodUtils.myMethod(new String[]{first, second, third, fourth, fifth});
(and in fact, you can invoke the latter).
You wouldn't expect an array's elements to change order in any other case, and nor do they with a variable arity parameter.
You can find this in JLS Sec 15.12.4.2:
If m is being invoked with k ≠ n actual argument expressions, or, if m is being invoked with k = n actual argument expressions and the type of the k'th argument expression is not assignment compatible with T[], then the argument list (e1, ..., en-1, en, ..., ek) is evaluated as if it were written as (e1, ..., en-1, new |T[]| { en, ..., ek }), where |T[]| denotes the erasure (§4.6) of T[].
Bonus question: What is the specifical name of such constructor
String... (in Java)?
Name of these type of constructors is parameterised constructor only.
The only difference is these are using Varargs.
Variable Argument (Varargs):
The varrags allows the method to accept zero or multiple arguments.
Before varargs either we use an overloaded method or take an array as the
method parameter but it was not considered good because it leads to
the maintenance problem. If we don't know how many arguments we will
have to pass in the method, var args is the better approach.
For More information
You will get these parameters in a order in which you have written , by referring your above example i can say ,
First will be first , and second will be second
As Java takes it as a string[]
Answer of bonus question
There is no specific name but the parameters you pass in such way is called as "var args"
hope you satisfied :)
According to my understanding, a method with variable length argument and another method with array as an argument are interchangeable. for eg.
void test(int ... ints){} //method-1
is same as
void test (int [] ints){} //method-2
and we can't use both in the same class (Compile Time Error).
When I use method-1, I can call this method by passing some integers or by passing an array of integers since calling this method using some integers will implicitly create an array of those integers.
For eg.
test(1,2,3); //ok
test(new int[5]); //ok
But,
When I use method-2, I can't call this method by passing some integers as arguments.
test(new int[5]); //ok
test(1,2,3); //NOT OK
Questions:
1) If java implicitly creates an array of the arguments, why it is not able to call test(1,2,3) in case of method-2.
2) Why java doesn't allow using method-1 and method-2 together when both show different behavior?
One difference I could think of is you can have additional arguments to a metod after an int[] but you cant have a variable argument like that. Variable argument should always be the last parameter to a method. Example
void test(int[] a, int a) {} // works fine.
void test(int ... ints, int a){} // compile error.
1) If java implicitly creates an array of the arguments, why it is not
able to call test(1,2,3) in case of method-2.
Because 1,2,3 in test(1,2,3) is not an array, these are three different integers...You have to pass an Array of int to invoke method-2.
2) Why java doesn't allow using method-1 and method-2 together when
both show different behavior?
It creates ambiguity when you call method with array as an argument because array as an argument is allowed in case of varargs as well as array as an argument.
for example, if both method were allowed, test(new int[5]) will create an ambiguity.
The internal representation in the Java compiler of the function declarations in these two cases is identical. That's why the compiler wont let you declare two such functions with the same name.
The difference in the bahivour of these two declarations can be seen when you actually call the functions with the actual arguments. Since Java is a strongly typed language (with strict type checking) it doesn't allow you to pass three integers to the test (int[] a) method.
However, when you call test(int ... a) with test(1,2,3) the compiler generates intermediate code to make a temporary array of three integers and passes it onto the function.
Why is it that, if you have, let's say, these functions:
void func1(Object o){
//some code
}
void func1(Object[] o){
//some code
}
You can call, for example:
func1("ABC");
but not :
func1({"ABC", "DEF"}); // instead having to write:
func1(new Object[]{"ABC", "DEF"});
Question: Is there any special reason why the constructor needs to be called on arrays ?
The "array initialiser" is only available for declarations / assignments:
Object[] o = { 1, 2 };
Or for "array creation expressions":
new Object[] { 1, 2 };
Not for method calls:
// Doesn't work:
func1({1, 2});
It's the way it is... You can read about it in the JLS, chapter 10.6. Array Initializers. An extract:
An array initializer may be specified in a declaration (§8.3, §9.3, §14.4), or as part of an array creation expression (§15.10), to create an array and provide some initial values.
Apart from it not being defined in the JLS right now, there seems to be no reason why a future Java version wouldn't allow array initialisers / array literals to be used in other contexts. The array type could be inferred from the context in which an array literal is used, or from the contained variable initialisers
Of course, you could declare func1 to have a varargs argument. But then you should be careful about overloading it, as this can cause some confusion at the call-site
There was a suggestion that Java SE 5.0 was going to have an array literal notation. Unfortunately, we got varargs instead, with all the fun that goes with that.
So to answer the question of why, the language is just like that. You may see list literals in a later version of Java.
You are trying to perform inline array initialization which Java doesn't really support yet.
I suppose you could achieve the desired result using varargs if you so wished, but if you need to pass in an array to a method, you have to initialise it the way Java likes an array to be initialised.
When you call func1("ABC") an object of type String with value"ABC" is created automatically by java.For creating any other object other than of type String you need to used the new operator.
Someone asked me what the difference between the two method parameters and why you would use the ... over specifically assigned array.
putMessage(byte ...send)
putMessage(byte[] send)
I couldn't answer them with confidence and couldn't remember what the ... is called.
The ... in your first example are called varargs. Your second example has an array argument. Varargs are a convenience for times when you want to hard code a variable number of arguments to a method but don't want to manually create an array to hold them. It's a shorthand notation. Consider this:
putMessage(0b00100101, 0b00100101, 0b00100101); // varargs
vs. this:
putMessage(new byte[] { 0b00100101, 0b00100101, 0b00100101 }); // array
The first example is less cluttered and more readable.
The parameters with ellipses are generally referred to as "varargs" if you want to google that.
Using varargs allows you to call a method with variable number of arguments without having to specify an array e.g.
public void printStr(String ...strings) {
for (String s : strings) {
System.out.println(s);
}
}
> printStr("Hello", "World")
Hello
World
So varargs allow a certain degree of convenience, but there are downsides - the varargs parameter must be the last parameter in the method signature, and thus you cannot have more than one varargs parameter to a method. If you want to pass multiple arrays to a method you have to use arrays, not varargs.
Another reason you might see arrays in some places where you might expect varargs is that varargs were only introduced in Java 5 - older code and code that needs to be backwards compatible will still be using arrays even where it might make more sense conceptually to use varargs.
The advantage of using varargs in the method signature is flexibility - there are some situations where the caller will have an array ready anyway and some where they will just have several arguments. Varargs will accept either the array or each variable as a separate argument, saving the caller the trouble of instantiating and populating an array.
The first one is with Varargs.
In short
A. First can be used to call with single byte type arg, 2 byte args.. or many args or an array.
B. second will be used with array only.
The ellipsis (three dots) indicates that you are using "varargs".
See http://download.oracle.com/javase/1,5.0/docs/guide/language/varargs.html for more details.
Inside the method, you access the elements of "send" as an array. The two methods are the same in that regard. The convenience is for the caller. In the second putMessage, the caller is compelled to create an array of bytes to pass to putMessage. In the first putMessage, the caller can simply say "putMessage(byte1, byte2)" or "putMessage(byte1, byte2, byte3)" or "putMessage(byte1)" -- variable number of arguments, or varargs.
The ellipses (...) allow you to inline N parameters of a type to a function call without having to define an array first. In the end you do simply get an array of parameters but it's basically shorthand or syntactic sugar. Also your client code might be a little cleaner and more declarative with the ellipses syntax... though it could easily go the other way and become mucky and unreadable.
Here's a great example of the ellipses syntax (variable length argument lists.) While looking at the sample consider what the client code (in the main function) would look like if an array was used instead of a variable length argument list.