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.
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());
Can I override any methods of array?
For example toString() or other methods.
import java.lang.reflect.Method;
public class ArraysClassTest {
static int[] array = { 1, 2, 3, 1 };
public static void main(String[] args) {
Class<? extends int[]> class1 = array.getClass();
try {
Method method = class1.getMethod("toString");
} catch (NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
You can't change any features of arrays. JLS §10.7 Array Members specifies every member of an array:
The members of an array type are all of the following:
The public final field length, which contains the number of components of the array. length may be positive or zero.
The public method clone, which overrides the method of the same name in class Object and throws no checked exceptions. The return type of the clone method of an array type T[] is T[].
A clone of a multidimensional array is shallow, which is to say that it creates only a single new array. Subarrays are shared.
All the members inherited from class Object; the only method of Object that is not inherited is its clone method.
The specification doesn't allow any way of customizing this implementation. An array's toString() method, for example, is always the basic one inherited from Object.
To create an array object the compiler emits one of three instructions into the compiled Java bytecode: newarray for primitives, anewarray for reference types, or multinewarray for all multidimensional arrays. In implementing those instructions, the virtual machine creates each array class as needed at runtime (JVMS §5.3.3 Creating Array Classes). The VM also defines dedicated bytecode instructions for the compiler to use for getting and setting elements of arrays and getting an array's length.
How the arrays are implemented within the VM is not specified whatsoever. It is purely an implementation detail, and even the Java compiler doesn't know, or care. The actual code involved depends on the flavor of virtual machine you're running your program on, the version of that VM, the OS and CPU it's running on, and any relevant runtime options the VM is configured with (e.g., whether in interpreted mode or not).
A quick look over the OpenJDK 8 source code turns up some of the relevant machinery for arrays:
src/share/vm/oops/arrayKlass.cpp
src/share/vm/oops/objArrayKlass.cpp
src/share/vm/oops/typeArrayKlass.cpp
src/share/vm/interpreter/bytecodeInterpreter.cpp – implements bytecode instructions for the interpreter, including instructions for creating and accessing arrays. It's tortuous and intricate, however.
src/share/vm/c1/c1_RangeCheckElimination.cpp – performs some clever array bounds check eliminations when compiling from bytecode to native code.
As arrays are a core feature of the language and the VM, it's impossible to point to any one source file and say "here, this is the class Array code". Arrays are special, and the machinery that implements them is literally all over the place.
If you want to customize the behavior of an array, the only thing you can do is not use the array directly, but use, subclass, or write, a collection class that internally contains the array. That gives you complete freedom to define the class's behavior and performance characteristics. However, it is impossible to make a custom class be an array in the Java language sense. That means you can't make it implement the [] operator or be passable to a method that expects an array.
In Java, all arrays (including those of primitive types) have java.lang.Object as their base class. (For one thing this is how zero length arrays can be modelled).
Although it's possible to override any method in that base class, Java itself specifies the form of the array. You are not able to interfere with that: in particular you can't extend an array.
To answer your direct question: no, you can't.
Arrays are a "compiler" construct - the compiler knows what String[] means; and it creates the corresponding byte code out of that. You can only create array objects, but not "new array classes". Or beyond that, the JVM knows what to do about "array using" bytecode instructions.
In other words: the source code that defines the behavior of Array-of-something objects is completely out of your control. Arrays just do what arrays do; no way for you to interfere with that.
And to get to your implicit question why things are this way:
Sometimes there isn't much to understand; but simply to accept. Thing is that the Java language was created almost 20+ years ago; and at some point, some folks made some design choices. Many of them were excellent; some of them might have been handled if we would redo things nowadays.
You will find for example, that Scala has a different way of dealing with arrays. But for java, things are as they are; and especially for things that are "so core" to the language as arrays, there is simply no sense in changing any of that nowadays.
You can create a proxy and use it in place of the original object
final int[] array = { 1, 2, 3, 1 };
Object proxy = Proxy.newProxyInstance(array.getClass().getClassLoader(), array.getClass().getInterfaces(), new InvocationHandler() {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
StringBuilder b=new StringBuilder("the array is");
for(int i:array)
b.append(" ").append(i);
return b.toString();
}
});
System.out.println(proxy.toString());
the output of the above is "the array is 1 2 3 1".
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.
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.
Let's say I have a method m() that takes an array of Strings as an argument. Is there a way I can just declare this array in-line when I make the call? i.e. Instead of:
String[] strs = {"blah", "hey", "yo"};
m(strs);
Can I just replace this with one line, and avoid declaring a named variable that I'm never going to use?
m(new String[]{"blah", "hey", "yo"});
Draemon is correct. You can also declare m as taking varargs:
void m(String... strs) {
// strs is seen as a normal String[] inside the method
}
m("blah", "hey", "yo"); // no [] or {} needed; each string is a separate arg here
You can directly write the array in modern Java, without an initializer. Your example is now valid. It is generally best to name the parameter anyway.
String[] array = {"blah", "hey", "yo"};
or
int[] array = {1, 2, 3};
If you have to inline, you'll need to declare the type:
functionCall(new String[]{"blah", "hey", "yo"});
or use varargs (variable arguments)
void functionCall(String...stringArray) {
// Becomes a String[] containing any number of items or empty
}
functionCall("blah", "hey", "yo");
Hopefully Java's developers will allow implicit initialization in the future
Update: Kotlin Answer
Kotlin has made working with arrays so much easier! For most types, just use arrayOf and it will implicitly determine type. Pass nothing to leave them empty.
arrayOf("1", "2", "3") // String
arrayOf(1, 2, 3) // Int
arrayOf(1, 2, "foo") // Any
arrayOf<Int>(1, 2, 3) // Set explict type
arrayOf<String>() // Empty String array
Primitives have utility functions. Pass nothing to leave them empty.
intArrayOf(1, 2, 3)
charArrayOf()
booleanArrayOf()
longArrayOf()
shortArrayOf()
byteArrayOf()
If you already have a Collection and wish to convert it to an array inline, simply use:
collection.toTypedArray()
If you need to coerce an array type, use:
array.toIntArray()
array.toLongArray()
array.toCharArray()
...
Another way to do that, if you want the result as a List inline, you can do it like this:
Arrays.asList(new String[] { "String1", "string2" });
You can create a method somewhere
public static <T> T[] toArray(T... ts) {
return ts;
}
then use it
m(toArray("blah", "hey", "yo"));
for better look.
Other option is to use ArrayUtils.toArray in org.apache.commons.lang3
ArrayUtils.toArray("elem1","elem2")
I'd like to add that the array initialization syntax is very succinct and flexible. I use it a LOT to extract data from my code and place it somewhere more usable.
As an example, I've often created menus like this:
Menu menu=initMenus(menuHandler, new String[]{"File", "+Save", "+Load", "Edit", "+Copy", ...});
This would allow me to write come code to set up a menu system. The "+" is enough to tell it to place that item under the previous item.
I could bind it to the menuHandler class either by a method naming convention by naming my methods something like "menuFile, menuFileSave, menuFileLoad, ..." and binding them reflectively (there are other alternatives).
This syntax allows AMAZINGLY brief menu definition and an extremely reusable "initMenus" method. (Yet I don't bother reusing it because it's always fun to write and only takes a few minutes+a few lines of code).
any time you see a pattern in your code, see if you can replace it with something like this, and always remember how succinct the array initialization syntax is!.
As Draemon says, the closest that Java comes to inline arrays is new String[]{"blah", "hey", "yo"} however there is a neat trick that allows you to do something like
array("blah", "hey", "yo") with the type automatically inferred.
I have been working on a useful API for augmenting the Java language to allow for inline arrays and collection types. For more details google project Espresso4J or check it out here