Using Java6 reflection API, Class.getDeclaredFields returns pretty weird values. Example of class field:
protected String[] arrF = new String[15];
Using getDeclaredFields on proper Class, a Field is returned:
name = arrF
type = [Ljava.lang.String;
The question: does [L prefix mean that the arrF is an array? Can I always rely on that, i.e. the field is an array iff type is prefixed with [L? If no, how can I get some information about "arrayness" of the field?
[ means one-dimension array ([[ is 2 dimensions array), and L followed by the class/interface name (Lclassname) is the type of that array.
See Class#getName().
Can I always rely on that, i.e. the field is an array iff type is prefixed with [L?
Yes. Or better, use Class#isArray().
This is a much better way to do it!
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import static java.lang.System.out;
public class ArrayFind {
public static void main(String... args) {
try {
Class<?> cls = Class.forName(args[0]);
Field[] flds = cls.getDeclaredFields();
for (Field f : flds) {
Class<?> c = f.getType();
if (c.isArray()) {
// ....
}
}
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
}
Btw. byte[] has name [B - [L is there for longs and/or references.
What you got is so called binary name. Binary name is able to represent an array type as arrays in Java do not implement or extend an Array-like interface.
[Ljava.lang.String; means:
[ single dimensional array
L array of reference types and not primitives
java.lang.String; represents the component type of the array
Related
import java.util.ArrayList;
import java.util.List;
public class Shelf {
private List<type> shelf = new ArrayList<>();
public void addItem(type item) {
shelf.add(item);
}
public type removeItem(int i) {
type output = shelf.get(i);
shelf.remove(i);
return output;
}
}
public class testclass {
public static void main(String[] args) {
Shelf<String> shelf = new Shelf();
String book = "Books";
shelf.addItem(book);
System.out.println(shelf.removeItem(0));
}
}
I'm trying to find out how I am able to initialize a type when I create a class so above example would work?
In shelf class whereever I have type, thats the type that the program would use.
A type parameter can be defined as follows:
public class Shelf<type> {
...
}
The type parameter section follows the class name and is delimited by angle brackets (< and >).
A type variable can be any non-primitive type you specify: any class type, any interface type, any array type, or even another type variable.
When you instantiate a Shelf, you indicate its type:
Shelf<String> shelf = new Shelf<>();
shelf.addItem("a string goes here");
Shelf<Integer> shelf = new Shelf<>();
shelf.addItem(42);
There are some conventions about the type parameter names. Instead of type you should use T:
public class Shelf<T> {
...
}
See this quote from The Java Tutorials from Oracle (highlight is mine):
By convention, type parameter names are single, uppercase letters.
This stands in sharp contrast to the variable naming conventions that
you already know about, and with good reason: Without this convention,
it would be difficult to tell the difference between a type variable
and an ordinary class or interface name.
The most commonly used type parameter names are:
E - Element (used extensively by the Java Collections Framework)
K - Key
N - Number
T - Type
V - Value
S, U, V etc. - 2nd, 3rd, 4th types
You can create a type as a generic by suffixing the type name with a list of type parameters:
public class Shelf<type> {
...
}
Now, anywhere inside of the Shelf class, you can refer to type whenever you want to refer to the type that the client specifies in angle braces.
(Customarily, you'd give that placeholder a name whose first letter was capitalized, like Type, to make clear that it's a type name.)
Object o = new Long[0]
System.out.println( o.getClass().isArray() )
System.out.println( o.getClass().getName() )
Class ofArray = ???
Running the first 3 lines emits;
true
[Ljava.lang.Long;
How do I get ??? to be type long? I could parse the string and do a Class.forname(), but thats grotty. What's the easy way?
Just write
Class ofArray = o.getClass().getComponentType();
From the JavaDoc:
public Class<?> getComponentType()
Returns the Class representing the component type of an array. If this class does not represent an array class this method returns null.
http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Class.html#getComponentType():
public Class<?> getComponentType()
Returns the Class representing the component type of an array. If this class does not represent an array class this method returns null...
#ddimitrov is the correct answer. Put into code it looks like this:
public <T> Class<T> testArray(T[] array) {
return array.getClass().getComponentType();
}
Even more generally, we can test first to see if the type represents an array, and then get its component:
Object maybeArray = ...
Class<?> clazz = maybeArray.getClass();
if (clazz.isArray()) {
System.out.printf("Array of type %s", clazz.getComponentType());
} else {
System.out.println("Not an array");
}
A specific example would be applying this method to an array for which the component type is already known:
String[] arr = {"Daniel", "Chris", "Joseph"};
arr.getClass().getComponentType(); // => java.lang.String
Pretty straightforward!
Please consider the following code :
class A {
B[] arr = new B[10];
private class B {}
}
class C {
void fun(){
A a = new A();
Object arr = a.arr;
Object len = a.arr.length; // !! ERROR
}
}
As I written in code. a.arr.length; is giving error.
I actually understand why it is happening. It is because sub class B is private. But still why it is happening. In class A, property arr was accessible, but why not it's length. Is there any explanation for this in jls or anywhere.
I just want a clear explanation for this behaviour. I know private things cannot be accessed outside of its class. But a public array could be. No matter of what type it is. And if anything is accessible outside, then its public properties should also be accessed. But here it is not happening.
Edit : I found that in C# it is not even possible to create an array of private class. In java if we cannot access anything, and cannot even know the length of the array of private class then what is the use of creating an array of private class.
The reason for this is a combination of two statements in the JLS:
Item 6.6.1 Determining accessibility:
An array type is accessible if and only if its element type is accessible.
This means that if T is private, T[] is also considered private.
Item 10.7 Array members:
The public final field length, which contains the number of components of the array. length may be positive or zero.
Remember that accessibility is determined at compile-time based on the type of reference you have, not on the type of the actual object!
Now, let's go into a little more elaborate example to demonstrate what this means. I added a toString() and a constructor to B.
class A {
B[] arr = { new B(1), new B(2), new B(3), new B(4) };
B plain = new B(99);
private class B {
public int i;
B(int i) {
this.i = i;
}
#Override
public String toString() {
return "Hidden class B(" + i + ")";
}
}
}
Now, in class C, we use:
A a = new A();
Object plain = a.plain;
String s = plain.toString();
This is legal, because a.plain is a visible field. s will contain Hidden class B(99). But if you try:
String s = a.plain.toString(); // Compile error
This will not be allowed, because althogh toString() in B is public, B itself is private, you have no access to its members, whether public or private.
Note that we cannot access i in B despite its being public. If we use:
plain.i
Then since i is not a member of Object, you get a compile error. And if we use:
a.plain.i
Then since a.plain is private, you can't access its members, as we already tried.
So now we go and look at the issue of arrays. Suppose we write:
Object[] objArr = a.arr;
int len = objArr.length;
This is legal, despite the fact that objArr is internally A.B[]. We have a reference to Object[], Object is public and so is Object[]. But:
int len = a.arr.length;
Gives you a compile error exactly as we got for a.plain.toString(). Although length is public in itself, you are accessing it through a reference to A.B[]. A.B[] is not accessible because A.B is not accessible. And therefore, because length is its member, you cannot access it. You simply cannot access any member of a reference type that is not visible to you, by the first rule above.
It is interesting to note that the following is legal:
Object firstItem = a.arr[0];
We can use the expression a.arr[0] because it is not considered an attempt to access a member of the array. The elements of the array are not considered to be members in it. a.arr[0] is simply an expression on an array reference that resolves to type A.B. There is no problem with such an expression as long as we don't try to access members of the item.
firstItem.toString() // Good
a.arr[0].toString() // Bad
Summary
It's OK to get hold to a reference to a private type, provided you cast it to some public supertype.
It's OK to get a specific item in an array of a private type. Indexing the array is not considered "accessing a member", it's just an expression on a reference that gives you a reference to its member type. Which you'll need to cast to something public in order to use.
It's not OK to try accessing a member with a given reference to a private type, even if the member is public. This includes the length of an array.
It's OK to access that public member through a cast to a supertype if it's available in that supertype. length is available in Object [] so you can get it through that.
It's not possible to access a public member of a private type that doesn't exist in an accessible supertype.
Do this:
class A {
B[] arr = new B[10];
public int getArrayLength()
{
return arr.length;
}
private class B {}
}
class C {
void fun(){
A a = new A();
Object arr = a.arr;
//Object isn't type safe
//Object len = a.getArrayLength();
int len = a.getArrayLength();
}
}
According to JavaDocs
At the member level, you can also use the public modifier or no modifier (package-private) just as with top-level classes, and with the same meaning. For members, there are two additional access modifiers: private and protected. The private modifier specifies that the member can only be accessed in its own class. The protected modifier specifies that the member can only be accessed within its own package (as with package-private) and, in addition, by a subclass of its class in another package.
Knows that the question is about accessing the length field. But, it was interesting for me to find that the length can be determined by enhanced-for-loop, not by making changes to access privileges or using reflection:
int length = 0;
for(Object o : a.arr) {
length++;
}
Few interesting statements about arrays were:
Arrays
In the Java programming language, arrays are objects (§4.3.1), are
dynamically created, and may be assigned to variables of type Object
(§4.3.2). All methods of class Object may be invoked on an array.
Array Types
An array's length is not part of its type.
My code declares a value variable of type Object:
final Object value;
This variable is then loaded with an object.
A generic collection variable is then declared and loaded:
final Collection<?> c = (Collection<?>) argumentDefinition.getFieldValue();
The collection variable is generic in both instances above, with brackets and a question mark that don't pass through in this text.
When I try to use the add method of the collection:
c.add(value)
I get the error message:
java: incompatible types:java.lang.Object cannot be converted to capture #1 of ?
The add method is declared in Collection as:
boolean add(E e);
How can I fix the error? I think I understand what's going on - the compiler creates a placeholder for the generic type that Object isn't compatible with. I can't use a raw type for the collection because I'm trying to eliminate raw types in the code. Do I need to use a helper function, and if so how exactly? Thank you.
It's hard to tell what exactly your problem is without knowing what argumentDefinition.getFieldValue() returns, but a possible solution would be change your variable type from Collection<?> to Collection<Object>.
You can replace ? with Object. i think it will work
import java.util.ArrayList;
import java.util.Collection;
public class KaviTemre {
final Object value="kavi";
public static void main(String[] args) {
new KaviTemre().myMethod();
}
void myMethod()
{
Collection<Object> obj = new ArrayList<Object>();
final Collection<Object> c = (Collection<Object>)obj;
c.add(value);
for(Object o:c)
System.out.println(o.toString());
}
}
just cast to E before -- this may solve the problem
boolean add(E (E)e);
You should do the following:
((Collection<Object>)c).add(value);
Then the code will compile and run.
import java.util.HashMap;
import java.util.Map;
public class Main
{
public static void main(String[] args)
{
Map<Integer,Class> map=new HashMap<Integer,Class>();
map.put(0,Main.class);
Class[] classes=(Class[])map.values().toArray();
for (Class c:classes)
System.out.println(c.getName());
}
}
I try cast in this line Class[] classes=(Class[])map.values().toArray(); but get exception.
Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.Class;
at Main.main(Main.java:11)
What is problem?
Change:
Class[] classes = (Class[]) map.values().toArray();
To:
Class[] classes = map.values().toArray(new Class[0]);
This gives information on which type of array to convert the Collection to. Otherwise, it returns an array of type Object (and that cannot be cast to an Class[]).
Quoted from the API documentation for Collection.toArray(T[] a):
Returns an array containing all of the elements in this collection; the runtime type of the returned array is that of the specified array. ...
Note that toArray(new Object[0]) is identical in function to toArray().
toArray() returns an Object[] [and not any object derived from Object[]]. Each element in this array is of type Class, but the array itself is not of type Class[]
You should cast each element in the array to Class instead of trying to cast the array, or use Collection.toArray(T[]) to avoid casting.
Use T[] toArray(T[] a) from Collection instead.
Use this code for listing out the values of map i.e. class names:
Object[] array = map.values().toArray();
for (Object object : array) {
System.out.println(object.toString());
}