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.
Related
This is meant to be a canonical question and answer for similar questions where the issue is a result of shadowing.
I've defined two fields in my class, one of a reference type and one of a primitive type. In the class' constructor, I try to initialize them to some custom values.
When I later query for those fields' values, they come back with Java's default values for them, null for the reference type and 0 for the primitive type. Why is this happening?
Here's a reproducible example:
public class Sample {
public static void main(String[] args) throws Exception {
StringArray array = new StringArray();
System.out.println(array.getCapacity()); // prints 0
System.out.println(array.getElements()); // prints null
}
}
class StringArray {
private String[] elements;
private int capacity;
public StringArray() {
int capacity = 10;
String[] elements;
elements = new String[capacity];
}
public int getCapacity() {
return capacity;
}
public String[] getElements() {
return elements;
}
}
I expected getCapacity() to return the value 10 and getElements() to return a properly initialized array instance.
Entities (packages, types, methods, variables, etc.) defined in a Java program have names. These are used to refer to those entities in other parts of a program.
The Java language defines a scope for each name
The scope of a declaration is the region of the program within which
the entity declared by the declaration can be referred to using a
simple name, provided it is visible (§6.4.1).
In other words, scope is a compile time concept that determines where a name can be used to refer to some program entity.
The program you've posted has multiple declarations. Let's start with
private String[] elements;
private int capacity;
These are field declarations, also called instance variables, ie. a type of member declared in a class body. The Java Language Specification states
The scope of a declaration of a member m declared in or inherited by a
class type C (§8.1.6) is the entire body of C, including any nested
type declarations.
This means you can use the names elements and capacity within the body of StringArray to refer to those fields.
The two first statements in your constructor body
public StringArray() {
int capacity = 10;
String[] elements;
elements = new String[capacity];
}
are actually local variable declaration statements
A local variable declaration statement declares one or more local variable names.
Those two statements introduce two new names in your program. It just so happens that those names are the same as your fields'. In your example, the local variable declaration for capacity also contains an initializer which initializes that local variable, not the field of the same name. Your field named capacity is initialized to the default value for its type, ie. the value 0.
The case for elements is a little different. The local variable declaration statement introduces a new name, but what about the assignment expression?
elements = new String[capacity];
What entity is elements referring to?
The rules of scope state
The scope of a local variable declaration in a block (§14.4) is the
rest of the block in which the declaration appears, starting with its
own initializer and including any further declarators to the right in
the local variable declaration statement.
The block, in this case, is the constructor body. But the constructor body is part of the body of StringArray, which means field names are also in scope. So how does Java determine what you're referring to?
Java introduces the concept of Shadowing to disambiguate.
Some declarations may be shadowed in part of their scope by another
declaration of the same name, in which case a simple name cannot be
used to refer to the declared entity.
(a simple name being a single identifier, eg. elements.)
The documentation also states
A declaration d of a local variable or exception parameter named n
shadows, throughout the scope of d, (a) the declarations of any other
fields named n that are in scope at the point where d occurs, and (b)
the declarations of any other variables named n that are in scope at
the point where d occurs but are not declared in the innermost class
in which d is declared.
This means that the local variable named elements takes priority over the field named elements. The expression
elements = new String[capacity];
is therefore initializing the local variable, not the field. The field is initialized to the default value for its type, ie. the value null.
Inside your methods getCapacity and getElements, the names you use in the in their respective return statements refer to the fields since their declarations are the only ones in scope at that particular point in the program. Since the fields were initialized to 0 and null, those are the values returned.
The solution is to get rid of the local variable declarations altogether and therefore have the names refer to the instance variables, as you originally wanted. For example
public StringArray() {
capacity = 10;
elements = new String[capacity];
}
Shadowing with constructor parameters
Similar to the situation described above, you may have formal (constructor or method) parameters shadowing fields with the same name. For example
public StringArray(int capacity) {
capacity = 10;
}
Shadowing rules state
A declaration d of a field or formal parameter named n shadows,
throughout the scope of d, the declarations of any other variables
named n that are in scope at the point where d occurs.
In the example above, the declaration of the constructor parameter capacity shadows the declaration of the instance variable also named capacity. It's therefore impossible to refer to the instance variable with its simple name. In such cases, we need to refer to it with its qualified name.
A qualified name consists of a name, a "." token, and an identifier.
In this case, we can use the primary expression this as part of a field access expression to refer to the instance variable. For example
public StringArray(int capacity) {
this.capacity = 10; // to initialize the field with the value 10
// or
this.capacity = capacity; // to initialize the field with the value of the constructor argument
}
There are Shadowing rules for every kind of variable, method, and type.
My recommendation is that you use unique names wherever possible so as to avoid the behavior altogether.
int capacity = 10; in your constructor is declaring an local variable capacity which shadows the field of the class.
The remedy is to drop the int:
capacity = 10;
This will change the field value. Ditto for the other field in the class.
Didn't your IDE warn you of this shadowing?
There are two parts to using variables in java/c/c++. One is to declare the variable and the other is to use the variable (whether assigning a value or using it in a calculation).
When you declare a variable you must declare its type. So you would use
int x; // to declare the variable
x = 7; // to set its value
You do not have to re-declare a variable when using it:
int x;
int x = 7;
if the variable is in the same scope you will get a compiler error; however, as you are finding out, if the variable is in a different scope you will mask the first declaration.
Another widely accepted convention is to have some prefix (or suffix - whatever you prefer) added to class members to distinguish them from local variables.
For example class members with m_ prefix:
class StringArray {
private String[] m_elements;
private int m_capacity;
public StringArray(int capacity) {
m_capacity = capacity;
m_elements = new String[capacity];
}
public int getCapacity() {
return m_capacity;
}
public String[] getElements() {
return m_elements;
}
}
Most IDEs have already available support for this notation, below is for Eclipse
I'm trying to implement a linked collection using generics, something like the following.
public class A<E> {
private class B {
private B[] b;
private E item;
private B() {
this.b = new B[2];
}
} // end inner class B
} // end class A
A is the collection and B an element or node in the collection with an array referencing successors/predecessors and an item.
The array creation is not allowed. The error I get is generic array creation. Am I right to think that what it's actually creating is an array of A<E>.B?
If not, what's causing the error?
If so, how can I get around this?
I have obviously omitted a substantial amount of code, if what I've provided is not enough please let me know. Any advice would be appreciated. Thank you.
EDIT 1: I should have mentioned that the parameterized type must be the same in A as in B. So passing <E> to the inner class is not possible, as it creates E#2 and leaves A with E#1.
You call B inherits the generic from the outer class, as it is not static. And you can't just make it static, because it will then need E also.
So your B.b array will indeed need a type that is generic, i.e. A<E>.B or if you'd change your code to a static inner class, A.B<E> (if you would use private static class B<E>).
In Java, due to the way generics are implemented (by erasure), the type of the array is not well-defined. On one hand, it should be an array of B, on the other hand, it should be an array of Object.
The most workable solution seems to be to use Object[] and cast explicitly.
If you want increased type safety, you can of course use an ArrayList<B>, which internally uses Object[], too!
In you particular code, B b1, b2; might also be an option which is actually faster (no bounds checking) and needs less memory (no array object; no size information).
B is a non-static inner class. That means it has a reference to an instance of the enclosing class. So it is implicitly parameterized by the type parameter of outer class. So when you write B, it means A<E>.B. To create an array, you should use the raw class. However, B is not the raw class; to refer to the raw class you need to explicitly qualify it: A.B
So this is that you want:
this.b = new A.B[2];
You need to pass E to the inner class as well
private class B<E> {
private B[] b;
private E item;
private B() {
this.b = new B[2];
}
} // end inner class B
I'm trying to implement a linked collection using generics, something like the following.
public class A<E> {
private class B {
private B[] b;
private E item;
private B() {
this.b = new B[2];
}
} // end inner class B
} // end class A
A is the collection and B an element or node in the collection with an array referencing successors/predecessors and an item.
The array creation is not allowed. The error I get is generic array creation. Am I right to think that what it's actually creating is an array of A<E>.B?
If not, what's causing the error?
If so, how can I get around this?
I have obviously omitted a substantial amount of code, if what I've provided is not enough please let me know. Any advice would be appreciated. Thank you.
EDIT 1: I should have mentioned that the parameterized type must be the same in A as in B. So passing <E> to the inner class is not possible, as it creates E#2 and leaves A with E#1.
You call B inherits the generic from the outer class, as it is not static. And you can't just make it static, because it will then need E also.
So your B.b array will indeed need a type that is generic, i.e. A<E>.B or if you'd change your code to a static inner class, A.B<E> (if you would use private static class B<E>).
In Java, due to the way generics are implemented (by erasure), the type of the array is not well-defined. On one hand, it should be an array of B, on the other hand, it should be an array of Object.
The most workable solution seems to be to use Object[] and cast explicitly.
If you want increased type safety, you can of course use an ArrayList<B>, which internally uses Object[], too!
In you particular code, B b1, b2; might also be an option which is actually faster (no bounds checking) and needs less memory (no array object; no size information).
B is a non-static inner class. That means it has a reference to an instance of the enclosing class. So it is implicitly parameterized by the type parameter of outer class. So when you write B, it means A<E>.B. To create an array, you should use the raw class. However, B is not the raw class; to refer to the raw class you need to explicitly qualify it: A.B
So this is that you want:
this.b = new A.B[2];
You need to pass E to the inner class as well
private class B<E> {
private B[] b;
private E item;
private B() {
this.b = new B[2];
}
} // end inner class B
In the following example, the variable b is declared final, but not static. That means it's a constant instance variable. However, because it's constant, none of the Passenger objects can change its value. So isn't it better to declare it static and make it a class variable, so that there is only one copy to be used by all instantiated objects?
class Passenger {
int a;
final int b = 0;
void drive() {
System.out.println("I'm driving!");
}
}
The purpose of final but non-static variables is to have an object-wide constant. It should be initialized in the constructor:
class Passenger {
final int b;
Passenger(int b) {
this.b = b;
}
}
If you are always assigning a constant literal value (0) to the final variable, it doesn't make much sense. Using static is preferred so that you are only having a single copy of b:
static final int b = 0;
BTW I don't think having default access modifier was your intention.
It depends on the purpose of b. Usually constants are there for a specific purpose. If you make it static you could accidentally change it in some instance of that class and that will affect all the others.
If you have multiple instances of Passenger class, I would go for making it static. While this has little benefit when talking about an int variable, this could save some memory if you have complex objects. This is because a static variable belongs to a class, not to an instance, thus memory space for it will be reserved only once, and it will be referred by the class object itself, not by the instances. Of course, you should be aware that having b as a static variable means that the changes made on this variable will be reflected on all the classes that access this variable, but since you made it final this won't be the case.
Note also that with the code you've written, classes in the same package as Passenger will be able to read the b value by accessing it via Passenger.b (if static).
In java, the static attribute basically means: associated with the type itself, rather than an instance of the type.
In other words you can reference a static variable without creating instances of that type... Whereas in the case of just using final you'd need to instantiate the class.
So, yes, to answer your question, I'd say that you're right. :)
A final primitive is the same as a static final primitive (except more efficient)
A final reference to an immutable object the same as a static final reference of the same.
A final reference to a mutable object is NOT the same as a static final reference of the same.
final int i = 0;
// same as
static final int = 0;
final String hi = "Hello";
// same as
static final String hi = "Hello";
final List<String> list = new ArrayList<String>();
// is NOT the same as
static final List<String> list = new ArrayList<String>();
The only time the last example is the same is when you have a singleton. It is fairly common for singletons to be written with a confusion of static and non static fields and methods as the difference is not obvious. :|
A final variable is defined when you need a constant, so you can assign a value just once.
Using static, instead, you are defining a variable shared by all the objects of that type (like a global variable) and it is not associated with a certain object itself.
In Java, is it possible to use a type variable, as an array element, inside an Interface?
I've tried as a filed type and as a cast operator, but always get the error
Cannot make a static reference to the non-static type A
interface ITest<A> {
A[] j; // Cannot make a static reference to the non-static type A
Object[] = (A[]) new Object[3]; // Cannot make a static reference to the non-static type A
}
Is there any case, where I am able to use the construct A[] inside the interface (and in an enum type?)?
class CTest<A> {
enum MyEnum {
F, G, H;
// something that uses A[] inside. Getting the same error as above
}
}
You can use a generic array type in an interface, like this:
public interface Foo<T> {
void doSomething(T[] array);
}
Your problem was you were trying to declare a field in an interface, which you basically can't do other than for constants. You can't declare a field of a generic array type in an interface, but I'd hope that you wouldn't want to anyway.
Admittedly type erasure makes the combination of arrays and generics somewhat awkward in various situations, but I think the above at least answers the question you posed.
Fields in interfaces are implicitly public, static and final, they're basically constants. And you can't have constants that depend on a type parameter because in Java parameters are removed from the type on compilation.
By the way, this is independent of whether you're using an array or not,
public interface X<T> {
T c = (T)new AnyType();
}
won't work either. And neither would
public class X<T> {
public static final T c = (T)new AnyType();
}