A String array can be declared and initialized in the following way:
String[] str = {"A", "B"};
but for a method which accepts a String array as argument, why can't the same be used there?
For example: if in the code below, i replace the call to show() from show(str); to show({"A" "B"});, it shows complier error. Why?
public class StringArray {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String[] str = {"A", "B"};
show(str);
}
static void show(String[] s) {
System.out.println(s[0] + s[1]);
}
}
The compiler errors shown are:
StringArray.java:9: illegal start of expression
show({"A", "B"});
^
StringArray.java:9: ';' expected
show({"A", "B"});
^
StringArray.java:9: illegal start of expression
show({"A", "B"});
^
StringArray.java:9: ';' expected
show({"A", "B"});
^
StringArray.java:9: illegal start of type
show({"A", "B"});
^
StringArray.java:11: class, interface, or enum expected
static void show(String[] s) {
^
StringArray.java:13: class, interface, or enum expected
}
^
7 errors
Also using show(new String[] {"A", "B"}); is allowed. How is new String[]{"A", "B"} different from {"A", "B"} when passing them as method arguments?
Thanx in advance!
The syntax {"A", "B"} (without new String[] in front of it) can only be used as an array initializer expression. In all other contexts (including method calls), you need to use the new operator.
See the Java Tutorial on Arrays for more info.
String[] str = {"A", "B"}; is minified version of String[] str = new String[]{"A", "B"};, Compiler doesn't know about plain {"A", "B"} as a string array unless you explicitly mention.
Short Answer
It has everything to do with memory management.
Long Answer
Background:
There is another question about passing arrays as arguments (marked as a duplicate) that is asking about the same behavior but is interested in a deeper 'why'.
Other answers have correctly explained the difference between
A)
new String[]{"A", "B"}
and
B)
{"A", "B"}
when passing them as method arguments.
A) constructs an instance of an array on the heap and the expression results in a reference to the instance.
B) is the syntax to define an array but that syntax is only valid during the initialization of a local array variable. This syntax is not an expression that can be evaluated by itself, it expects there to be an array already instantiated but uninitialized where this block is then used as the initializer.
Blame it on Java
All this has already been mentioned, so what I'd like to answer is the why behind the language design decision that puts this behavior in place.
One of the basic tenets of Java is that it manages memory to really minimize the huge problems that introduces when each programmer has to understand all the ins and outs, all the edge cases with dynamic memory management. So, when they designed the language's type system they would have liked to have every variable be a reference type but for efficiency they allowed some basic types that can be passed by value as an argument to a method, where the result is a simple clone of the contents of the variable, these are called primitive types, int, char, etc. All other types require a reference to the heap, which allows for good efficiency in parameter passing, these are called reference types. Reference types, as the name implies, are actually a reference to memory that has been allocated usually on the heap but can be memory on the stack.
Array Initializers
Ok, that's it for the Java Primer, but why is that important? It's because when you attempt to pass an array as in argument but use the literal syntax, the language expects a reference type but the array initializer construct does not resolve to a reference.
Now, the next question might be whether it would have been possible for the compiler to take the initializer syntax and transform it into a properly allocated instance of an array. That would be a fair question. The answer goes back to the the syntax that uses the initializer clause:
String[] str = {"A", "B"}
If you only have the expression on the right-hand-side of the equals sign, how do you know what type of array should be constructed? The simple answer is you don't. If we took that same initializer and used it like this
Circle[] cir = {"A", "B"}
it becomes more clear why this is the case. First you might notice the 'new' keyword seems to be missing. It's not missing but is implicitly being included by the compiler. This is because the initializer syntax is a short form of the following code
Circle[2] cir = new Circle[]();
cir[0] = new Circle("A");
cir[1] = new Circle("B");
The compiler uses the constructor for the array variable to instantiate each element of the array based on the list provided, So when you try to pass
{"A", "B"}
the compiler has no information about what type of array should be constructed nor does it know how to construct the individual elements of the array, hence the need to use the form that explicitly allocates memory.
For the Student of Languages
This separation between the type of the reference and the type of each element of the array is also what allows the array type to be a parent type of the elements, such as
Circle[2] shapes = new Circle[]();
shapes[0] = new Circle(); // parent
shapes[1] = new Ellipse(); // child of Circle
and Java's use of a parent class, Object for all classes allows arrays with completely unrelated objects
Object[2] myThings = new Object[]();
myThings[0] = new Car();
myThings[1] = new Guitar(); // unrelated object
When you pass {"A", "B"}, there is no object referencing to it because that array is not yet created in memory and that reference is needed to be passed.
But we can pass a string like "A" directly [without a reference] to a method accepting String, because String is java's special object for which String pool is maintained. and that is not the case with array which is like simple java object.
Since String[] str = {"A", "B"}; defines that it is an array of strings, {"A", "B"} nowhere says this is an array of string as an array of objects could be defined like that too, thus compiler doesn't know what type of array you are referring to :)
It works if you do it like this
public class StringArray
{
/**
* #param args
*/
public static void main(String[] args)
{
show(new String[]{"A", "B"});
}
static void show(String[] s)
{
System.out.println(s[0] + s[1]);
}
}
because you are actually creating a new array "object". The other way, {"A", "B"} doesn't mean anything. {"A", "B"} isn't an array object, so it won't work. The first way works because you are actually specifying that what is being passed to the function is an array object.
show({"A" "B"}); this expression is not passing array object. Passing array object You have to first declare and intialize the array object then passing array reference into method.
String[] str = {"A", "B"};
show(str);
OR
String[] str = {"A", "B"};
show(new String[]{"A", "B"});
In the above example, the signature of the show() method guides the compiler that it is expecting a String reference at the time of calling, thus we can pass only a reference variable of type String while calling the show() method. On the other hand {"A","B"} is just an expression not a reference that's why it is giving a compilation error like "Illegal Start of Expression".
Related
Please look at the code below.
class Use
{
public static void main(String[] args)
{
String[] result = new String[2];//{"Hello", "World"}; // compiles and run
Object[] name = result; // compiles and run
// result = name; //CE
System.out.println(result);
System.out.println(name);
}
}
My understanding is that arrays in Java are independent objects, with no parent child relationship. So according to me "result" and "name" are reference variables of two completely different types of objects (i.e String[] and Object[]) that do not have any relationship. Then
why there is not an error in the statement "Object[] name = result;"?
why there is an error in the statement "result = name;"?
I know "Object" and "String" have a parent-child relationship. Is there any relationship between "Object[]" and "String[]"?
result = name is trying to assign an Object array to a String array. You cannot assign an Object to a variable that was declared as a String, and the same goes for arrays. You cannot assign an array to a variable that was declared as an array of a different type.
Object[] name = result is legal because all objects inherit from the Object class, including String. You can assign a different data type to a declared variable if the what you are assigning inherits or implements the data type of the variable you are assigning to.
To gain a better understanding, look through the documentation of the Java language. It is a great resource for seeing the relationships between different classes.
Object class documentation: https://docs.oracle.com/javase/7/docs/api/java/lang/Object.html
String class documentation: https://docs.oracle.com/javase/7/docs/api/java/lang/String.html
Integer class documentation: https://docs.oracle.com/javase/7/docs/api/java/lang/Integer.html
java.lang.Object class is the super base class of all Java classes. I guess that fixes your confusion. (if B extends C, it means B is a C and not C is a B.)
result = name; throws compilation error as they are of different types. You need to cast it to avoid compilation errors.
result = (String[]) name;
If it was another way round, name=result would have worked as String[] can be assigned to Object[]; not the other way.
https://javapapers.com/java/why-object-is-super-class-in-java/
How can String array references be stored in the reference variables of Object array?
I know "Object" and "String" have a parent-child relationship. Is there any relationship between "Object[]" and "String[]"?
According to JLS Section 4.10.3. Subtyping among Array Types String[] is a Object[] because String is a Object, and thus Object[] name = result; is legal.
Why there is an error in the statement "result = name;"?
Given the above, a Object[] is not a String[], that's why you'll get this error at compile-time.
Error:(13, 26) java: incompatible types: java.lang.Object[] cannot be converted to java.lang.String[]
(After all, an array of objects Object[] can contain elements of any reference type like String, Integer, etc.)
However, based on your code "you know" that actually this Object[] name array only contains Strings. So that Object[] name is actually a String[]. So you can make it work by explicitly using a downcast, i.e. result = (String[]) name.
By using the downcast you explicitly state to the compiler that this type conversion is fine (because in general it wouldn't be). Should you then break "your promise" in your code by doing this
String[] strings = {"Hello", "World"};
Object[] objects = strings;
objects[0] = 1; // let's replace the string with an integer
strings = (String[]) objects;
you'll get this exception at run-time.
Exception in thread "main" java.lang.ArrayStoreException: java.lang.Integer
I'm trying to call the main method on a Java class identified by a String. In the actual program, the class identifier will be a variable, but for the moment I'm trying this. The desired parameter main, newargs, is of type String[].
try {
Class c = Class.forName("Arena");
Class[] aarg = new Class[1];
aarg[0] = String[].class;
Method m = c.getMethod("main", aarg); // Needs parameter class list
m.invoke(newargs);
}
catch (Exception e) {
System.err.println(e);
}
First, I get a compiler warning (Athena is the name of the class I'm currently working in):
Note: Athena.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
Then on testing I get this exception:
java.lang.IllegalArgumentException: wrong number of arguments
I feel like the primary thing is I don't know how to identify the parameter of main() as an array of the String class (failed to find an example of that), but maybe I'm missing more than that.
In getMethod() you supply an array of Classes with length 1. This is somewhat incorrect; it should work due to the use of varargs, but it's pointless. The method will interpret it the same as simply providing one class. Literally adding .class to the expected types and separating them with commas should yield the proper result. If you had String[] and int as parameters in main(), it would look like c.getMethod("main", String[].class, int.class).
In invoke(), you can ignore the first parameter and pass null since main() is a static method. The second is declared as a varargs parameter, but in my test this didn't work properly. I cast to a single Object instead of an array of length 1 of Objects. Here, Object works as a raw type since invoke() is uninformed as to what its parameters should be at compile time, but can cast them to the desired type at runtime.
The varargs use in invoke() doesn't work in this case because it expects the type Object[] or comma separated values that can be combined to an Object[]. The issue is that String[] is a subclass of Object[]. So instead of interpreting String[] as a single String[] parameter, the invoke() method thinks you are trying to give it a set of distinct String/Object inputs.
Internal logic:
method.invoke(null, 1, 2, 3) becomes Object[] with int elements. 3 separate int parameters
method.invoke(null, "hello", "wow", "ok") becomes Object[] with String elements. 3 separate String parameters
method.invoke(null, String[] { "hello", "wow", "ok" }) becomes Object[] with String elements. 3 separate String parameters, even though we only desire one parameter that is String[]
Honestly this isn't your fault, the API is lacking good documentation to diagnose these issues. Feel free to ask me questions, this stuff is confusing.
try {
Class<?> c = Class.forName("Test");
Method m = c.getMethod("main", String[].class); // Needs parameter class list
String[] input = new String[] { "hello world" };
m.invoke(null, (Object) input);
}
catch (Exception e) {
e.printStackTrace();
}
i think, it would work if type of aarg was String[]. It is Class[] now.
invoke accepts two arguments,
public Object invoke(Object obj, Object... args)
obj the object the underlying method is invoked from args the
args arguments used for the method call
Class c = Class.forName("Arena");
String[] params = null;
Method m = c.getMethod("main", String[].class); // Needs parameter class list
m.invoke(null,(Object) params);
Note null for invoke as main method don’t need instance.
Class<? extends Demo> clazz = Demo.class;
Method mainMethod = clazz.getDeclaredMethod("main", String[].class);
final Object[] args = new Object[1];
args[0] = new String[]{"1", "2"};
mainMethod.invoke(null, args);
We can use any class name instead of Demo.
This question already has answers here:
Why are arrays covariant but generics are invariant?
(8 answers)
Closed 4 years ago.
I come from a C background. It doesn't make sense to me that I can't add an Object to a Object[] in foo().
I would expect the code in function foo to work at runtime but fail in main() when I attempt to do strings[0].toLowerCase(); because the object in strings[0] is not a String but is actually a StringBuilder which doesn't have a toLowerCase(). At the end of the day, I am storing a pointer into an array of pointers and the line objects[0] = other should not be invalid. It is clear my understanding of java is wrong (but I an fairly new at the moment).
class Main {
static void foo(Object[] objects, Object other) {
objects[0] = other;
}
public static void main(String[] args) {
String[] strings = { "stringValue" };
foo(strings, new StringBuilder());
}
}
EDIT: Thanks all for the answer. I googled "java covariance of arrays" thanks to #Andy Turner and came across this which discusses this behaviour. The trick is that the compiler and runtime treat the code very differently. Compiler is fine with the code, runtime isn't.
From the documentation of ArrayStoreException:
Thrown to indicate that an attempt has been made to store the wrong type of object into an array of objects. For example, the following code generates an ArrayStoreException:
Object x[] = new String[3];
x[0] = new Integer(0);
Just because you're passing a String[] into a method that expects an Object[] doesn't mean that it's actually an Object[]. Because you create a String[], you can only add instances of String to it, and you're trying to add a StringBuilder, which doesn't make sense.
Notice how it does not throw an exception if you change the type of String[] to Object[]:
public static void main(String[] args) {
Object[] strings = { "stringValue" };
foo(strings, new StringBuilder()); // Perfectly fine
}
It is evaluated at runtime that your String Array is in fact trying to assign a different type. You would an ArrayStoreException, which clearly says that
Thrown to indicate that an attempt has been made to store the wrong
type of object into an array of objects. For example, the following
code generates an ArrayStoreException:
Object x[] = new String[3];
x[0] = new Integer(0);
It is not generally a good practice to accept Object types as parameters or even return values of methods/functions. They could be polymorphic interfaces, but Object is at the highest levels of abstraction, and is not ideal for this purpose.
You are trying to add the Object "StringBuilder" to an Array of the object "String". Under some circumstances this might actually work, if you were to assign the StringBuilder Value but I am not to shure about that, as I never used this myself.
Here's a good read: https://docs.oracle.com/javase/7/docs/api/java/lang/StringBuilder.html
Usually Java takes care of pointers for you which makes it really hard to mess it up.
I hope I could help you :)
I am not new to Java Collections, but I have a confusion about the following scenario.
In my project I've implemented a code like this:
List<String> obj_lStr= new ArrayList<String>();
String[] obj_arrStr = {"someString", "noString", "abcString"};
obj_lStr.addAll(Arrays.asList(obj_arrStr));
but during code review my project lead gave me the instruction to change this code and implement it without using String[] obj_arrStr.
Then I changed my code to this:
obj_lStr.addAll(Arrays.asList( { "someString", "noString", "abcString" }));
but I got compilation errors:
Main.java:13: error: illegal start of expression
x.addAll(Arrays.asList({"someString", "noString", "abcString"}));
^
Main.java:13: error: ')' expected
x.addAll(Arrays.asList({"someString", "noString", "abcString"}));
^
Main.java:13: error: ';' expected
x.addAll(Arrays.asList({"someString", "noString", "abcString"}));
and I change my line of code to this:
obj_lStr.addAll(Arrays.asList("someString", "noString", "abcString"));
then the compilation error is gone.
Question: Why is it so? Why asList() method raises a compilation error with {"","",""}, but not for ("","","")?
Arrays.asList takes either an array, or all elements of the array as argument.
So you need either
Arrays.asList(new String[]{"someString", "noString", "abcString"})
or
Arrays.asList("someString", "noString", "abcString")
the signature of asList() has the answer :public static <T> List<T> asList(T... a)
it takes a varargs , so obj_lStr.addAll(Arrays.asList("someString", "noString", "abcString")); compiles bcoz your arguments will be turned into an array.
but here obj_lStr.addAll(Arrays.asList( { "someString", "noString", "abcString" }));, you need to specify the type of the array. so you can do this : obj_lStr.addAll(Arrays.asList(new String[] { "someString", "noString", "abcString" }));.
the {} initialization syntax for arrays is available only during initilization , for ex:
int[] a ={1,2,4}; // works
but
int[] a new int[3];
a = {1,2,4}; // doesn't work
It's just to do with where in Java code you're allowed to omit the type and allow the array to be inferred -- specifically, you can only use array = {foo, bar...} when you're assigning directly into an array of the appropriate type, not where you're passing it around elsewhere.
In any event, what you should be writing is
Arrays.asList("someString", "noString", "abcString")
with no {} braces at all.
you can either feed an String Array to Arrays.asList:
Arrays.asList(new String[] {"a","b","c"});
or with multiple Strings (varags)
Arrays.asList("a","b","c");
But just
Arrays.asList({"a","b","c"});
isnt working, as {"a","b","c"} does not represent an object an Java
If you try looking at the javadocs
, you'll learn that asList has the signature
public static <T> List<T> asList(T... a)
Now, the argument declaration T... a, uses a feature of java called variable arguments, which basically just means that the argument a, can either be an array of type T, or, it can be a sequence of objects all of type T.
Hope that helps.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
varargs and the '…' argument
Java, 3 dots in parameters
I saw this definition in my android java file.
It looks just like String[]. Are they different?
Thank you.
varags. If a method signature is method(Param param, String... x) it will take one Param type of object and any number of String objects.
There are couple if cool things about it:
It's nothing special. It's just sugared array. So, method(MyObject... o) is same as method(MyObject[] o).
Vararg has to be the last parameter in argument list.
There is this funny thing that bit me once. method(MyObject... o) can be called as method() without any compilation error. Java will internally convert the no-arg call to method(new MyObject[0]). So, be aware of this.
It's syntax for writing the items of the array as a parameter
for instance:
public String first (String... values) {
return values[0];
}
Then you can call this method with first("4","2","5","67")
The javacompiler will create an array of the parameters on its own.
It's a vararg, variable number of arguments. In the method body you treat it as a String[], but when you call the method you can either chose to supply a String[] or simply enumerate your values.
void foo(String... blah) { }
void bar() {
String[] a = { "hello", "world" };
foo(a); // call with String[]
foo("hello", "world"); // or simply enumerate items
}
Was introduced with Java 5.
It's for defining a method with a variable number of arguments.
String is a string type.
String[] is an array of strings.
String ... is a syntactic sugar named ellipsis, introduced in java 1.5 and taken from C. It can be used in methods definitions and actually the same as array with only one difference.
If method is defined as:
public void foo(String[] arg){}
you must pass array to it:
foo(new String[] {"a", "b"});
If method is defined as:
public void foo(String arg){}
You can call it either
foo(new String[] {"a", "b"});
or
foo("a", "b");