The following constellation:
public class AccessModifierTest {
private static class Super {
private int n = 3;
private Super() {}
private void setN(int n) {
this.n = n;
}
}
private static class Sub extends Super {
private Sub() {
super(); // invoking private ctor is permitted
// setN(5); but invoking private method is not permitted, why?
// super.setN(5); this is fine too
}
}
public static void main(String[] args) {
Super superInstance = new Sub();
}
}
The private ctor of Sub is allowed to invoke the private ctor of Super.
Why this is ok, is answered here:
scope of private constructor in Nested Class
Because of this, shouldn't it be also fine to invoke the private method setN in the ctor of Sub?
Why is this not legal?
Just like the superclass constructor, setN is perfectly accessible in Sub. This is because, as your linked Q&A explained, private members declared in an inner class are accessible everywhere as long as you are inside the top-level class. So the problem is somewhere else.
If you are using IntelliJ, the error message it gives is "setN has private access...", which is more useful and lets you fix the error immediately (and setN being private also plays a role as you will see). But javac's error message, "cannot find symbol setN", shows that this isn't actually about accessibility, and that the compiler can't actually find setN.
According to the spec, a simple method name like setN
consists of a single UnqualifiedMethodIdentifier which specifies the name of the method to be invoked. The rules of method invocation require that the UnqualifiedMethodIdentifier denotes a method that is in scope at the point of the method invocation.
setN is actually not in scope in the constructor of Sub. The scope of setN is only Super. See also Scope of a Declaration in the spec.
But doesn't Sub inherit setN from Super? Well, it doesn't. private members are not inherited. See this section for the conditions for inheritance.
A class C inherits from its direct superclass type D all concrete
methods m (both static and instance) for which all of the following
are true:
m is a member of D.
m is public, protected, or declared with package access in the same
package as C.
No method declared in C has a signature that is a subsignature
(§8.4.2) of the signature of m as a member of D.
If you use another form (instead of a simple name) to refer to Super.setN, like super.setN(5);, or ((Super)this).setN(5); then it can compile just fine.
For why these work, see the compile-time step 1 of processing a method call, where the compiler determines in which type it should search for a method declaration. If the type to search is Super, then it would compile, because Super actually has the setN method, whereas Sub does not inherit it from Super, so does not have it as a member.
Note that constructor calls and method calls are treated completely separately by the language spec and they have completely different rules. The super(); syntax is essentially "hardcoded" into the language - there is no "scope" or "names" of a constructor or anything like that. The constructor just has to be accessible for you to call it.
Compiler understands the scope of the class Super is within the AccessModifierTest, but compiler does not know a callee method of any class without the reference. This is how oop based languages are designed to work. The main benefit is that compiler can remove the method call ambiguity through this.
So when a private constructor is called within a class scope compiler understands where it is coming from, but any other method requires the object reference to access.
to make your code work you need to use the reference super.setN(5) to make it work.
Related
I am just trying to figure out why it is possible to do such thing but it is not possible in some other programming languages like C++.
Here is an example of what I am asking:
public class A{
private A objectOfTheSameClass;
public A()
{
objectOfTheSameClass=new A();
}
}
Because it's not explicitly prohibited.
JLS section 8.8.7 that defines that body of a constructor does not mandate a compile-time error for such a case. The only case when a compile-time error should happen is when a constructor invokes itself with this:
It is a compile-time error for a constructor to directly or indirectly invoke itself through a series of one or more explicit constructor invocations involving this.
More precisely:
Except for the possibility of explicit constructor invocations, and the prohibition on explicitly returning a value (§14.17), the body of a constructor is like the body of a method (§8.4.7).
Since a method is allowed to invoke itself (recursion), a constructor is also allowed to do the same.
This will not compile because it calls this:
public class A{
private A objectOfTheSameClass;
public A() {
objectOfTheSameClass= this();
}
}
Your code, although it compiles, will throw a StackOverflowError because the constructor of A creates itself a new instance of A. The compiler can't detect every infinite loop in your code...
It can be useful sometimes, for example in the "singleton" pattern is used to have exactly one instance of a class by creating an object of the same class as a class field, making it accessible via a public method and making the constructor private.
The instances are not created until the runtime, so the class is fully defined. It's up to you to use this in a proper way.
Why cant we declare an instance method in sub Class B which shares the same signature of a static method in parent Class A?
It throws a compile time error if i try to do that.
My question is, since static method of parent class is restricted to parent class, why does instance method of child class does not compile.
Lets see by code:
`
public class A{
static void testStatic(){}
}
public class B extends A{
void testStatic (){}
}
public class Test{
public static void main (String[] args){
A a = new B()
a.testStatic();
}
`
In the above code,since A does not have an instance method by that name, and since Java allows static methods to be accessed by objects, Object a of type 'A' pointing to 'B' can call static method present in it(class A). But complier throws an error "The instance method cannot override a static method" why?
Note: I can understand if a class does not allow same method name for two methods, even if one is instance and other is static. But I fail to understand why it does not allow a sub class to have an instance of same name. Especially considering the fact that static methods cannot be overridden. And yet, Java allows subclass to have same name as parent class static method, which is called information hiding, but not overriding.
The compiler throws an error because those are the rules of the language. From the Java Language Specification §8.4.8.2:
If a class C declares or inherits a static method m, then m is said to hide any method m', where the signature of m is a subsignature (§8.4.2) of the signature of m', in the superclasses and superinterfaces of C that would otherwise be accessible to code in C.
It is a compile-time error if a static method hides an instance method.
(emphasis in the original). The language is dense (as in most places in the JLS) but it matches the situation you are describing. The JLS doesn't provide a rationale for this rule that I could find on first reading. But a little thought about how one might try to make this rule unnecessary shows why it's there.
It's illegal in Java. The call to the static method is allowed on an instance as well, so there'd be no way to distinguish which method to call in some cases:
A a = new A ();
B b = new B ();
A.testStatic (); // ok
B.testStatic (); // ok
a.testStatic (); // ok calling static
b.testStatic (); // undefined. What to call? Based on what?
Last call is the reason why it's not allowed.
Calls to static methods are resolved at compile time itself. So, they cannot be overridden. And by defining another method with the same signature as the static method, the compiler complains.
static method is bound with class whereas instance method is bound with object.
Static belongs to class area and instance belongs to heap area.
I am not hundred percent sure but I guess answer as below.
Static method means it can be used without an instance of the the class in which it is defined. Also static method can access only static variables of the class. Now if we override non static method and create an instance of sub class with reference of the super class, compiler will be confused for above two basic functioning of static method. Please debate if any thing wrong in this.
4 line showing error. so do this
public class B extends A
Given the following classes:
public abstract class Super {
protected static Object staticVar;
protected static void staticMethod() {
System.out.println( staticVar );
}
}
public class Sub extends Super {
static {
staticVar = new Object();
}
// Declaring a method with the same signature here,
// thus hiding Super.staticMethod(), avoids staticVar being null
/*
public static void staticMethod() {
Super.staticMethod();
}
*/
}
public class UserClass {
public static void main( String[] args ) {
new UserClass().method();
}
void method() {
Sub.staticMethod(); // prints "null"
}
}
I'm not targeting at answers like "Because it's specified like this in the JLS.". I know it is, since JLS, 12.4.1 When Initialization Occurs reads just:
A class or interface type T will be initialized immediately before the first occurrence of any one of the following:
...
T is a class and a static method declared by T is invoked.
...
I'm interested in whether there is a good reason why there is not a sentence like:
T is a subclass of S and a static method declared by S is invoked on T.
Be careful in your title, static fields and methods are NOT inherited. This means that when you comment staticMethod() in Sub , Sub.staticMethod() actually calls Super.staticMethod() then Sub static initializer is not executed.
However, the question is more interesting than I thought at the first sight : in my point of view, this shouldn't compile without a warning, just like when one calls a static method on an instance of the class.
EDIT: As #GeroldBroser pointed it, the first statement of this answer is wrong. Static methods are inherited as well but never overriden, simply hidden. I'm leaving the answer as is for history.
I think it has to do with this part of the jvm spec:
Each frame (§2.6) contains a reference to the run-time constant pool (§2.5.5) for the type of the current method to support dynamic linking of the method code. The class file code for a method refers to methods to be invoked and variables to be accessed via symbolic references. Dynamic linking translates these symbolic method references into concrete method references, loading classes as necessary to resolve as-yet-undefined symbols, and translates variable accesses into appropriate offsets in storage structures associated with the run-time location of these variables.
This late binding of the methods and variables makes changes in other classes that a method uses less likely to break this code.
In chapter 5 in the jvm spec they also mention:
A class or interface C may be initialized, among other things, as a result of:
The execution of any one of the Java Virtual Machine instructions new, getstatic, putstatic, or invokestatic that references C (§new, §getstatic, §putstatic, §invokestatic). These instructions reference a class or interface directly or indirectly through either a field reference or a method reference.
...
Upon execution of a getstatic, putstatic, or invokestatic instruction, the class or interface that declared the resolved field or method is initialized if it has not been initialized already.
It seems to me the first bit of documentation states that any symbolic reference is simply resolved and invoked without regard as to where it came from. This documentation about method resolution has the following to say about that:
[M]ethod resolution attempts to locate the referenced method in C and its superclasses:
If C declares exactly one method with the name specified by the method reference, and the declaration is a signature polymorphic method (§2.9), then method lookup succeeds. All the class names mentioned in the descriptor are resolved (§5.4.3.1).
The resolved method is the signature polymorphic method declaration. It is not necessary for C to declare a method with the descriptor specified by the method reference.
Otherwise, if C declares a method with the name and descriptor specified by the method reference, method lookup succeeds.
Otherwise, if C has a superclass, step 2 of method resolution is recursively invoked on the direct superclass of C.
So the fact that it's called from a subclass seems to simply be ignored. Why do it this way? In the documentation you provided they say:
The intent is that a class or interface type has a set of initializers that put it in a consistent state, and that this state is the first state that is observed by other classes.
In your example, you alter the state of Super when Sub is statically initialized. If initialization happened when you called Sub.staticMethod you would get different behavior for what the jvm considers the same method. This might be the inconsistency they were talking about avoiding.
Also, here's some of the decompiled class file code that executes staticMethod, showing use of invokestatic:
Constant pool:
...
#2 = Methodref #18.#19 // Sub.staticMethod:()V
...
Code:
stack=0, locals=1, args_size=1
0: invokestatic #2 // Method Sub.staticMethod:()V
3: return
The JLS is specifically allowing the JVM to avoid loading the Sub class, it's in the section quoted in the question:
A reference to a static field (§8.3.1.1) causes initialization of only the class or interface that actually declares it, even though it might be referred to through the name of a subclass, a subinterface, or a class that implements an interface.
The reason is to avoid having the JVM load classes unnecessarily. Initializing static variables is not an issue because they are not getting referenced anyway.
The reason is quite simple: for JVM not to do extra work prematurely (Java is lazy in its nature).
Whether you write Super.staticMethod() or Sub.staticMethod(), the same implementation is called. And this parent's implementation typically does not depend on subclasses. Static methods of Super are not supposed to access members of Sub, so what's the point in initializing Sub then?
Your example seems to be artificial and not well-designed.
Making subclass rewrite static fields of superclass does not sound like a good idea. In this case an outcome of Super's methods will depend on which class is touched first. This also makes hard to have multiple children of Super with their own behavior. To cut it short, static members are not for polymorphism - that's what OOP principles say.
According to this article, when you call static method or use static filed of a class, only that class will be initialized.
Here is the example screen shot.
for some reason jvm think that static block is no good, and its not executed
I believe, it is because you are not using any methods for subclass, so jvm sees no reason to "init" the class itself, the method call is statically bound to parent at compile time - there is late binding for static methods
http://ideone.com/pUyVj4
static {
System.out.println("init");
staticVar = new Object();
}
Add some other method, and call it before the sub
Sub.someOtherMethod();
new UsersClass().method();
or do explicit Class.forName("Sub");
Class.forName("Sub");
new UsersClass().method();
When static block is executed Static Initializers
A static initializer declared in a class is executed when the class is initialized
when you call Sub.staticMethod(); that means class in not initialized.Your are just refernce
When a class is initialized
When a Class is initialized in Java After class loading, initialization of class takes place which means initializing all static members of class. A Class is initialized in Java when :
1) an Instance of class is created using either new() keyword or using reflection using class.forName(), which may throw ClassNotFoundException in Java.
2) an static method of Class is invoked.
3) an static field of Class is assigned.
4) an static field of class is used which is not a constant variable.
5) if Class is a top level class and an assert statement lexically nested within class is executed.
When a class is loaded and initialized in JVM - Java
that's why your getting null(default value of instance variable).
public class Sub extends Super {
static {
staticVar = new Object();
}
public static void staticMethod() {
Super.staticMethod();
}
}
in this case class is initialize and you get hashcode of new object().If you do not override staticMethod() means your referring super class method
and Sub class is not initialized.
I have 2 classes as below
public class statictest {
public void print()
{
System.out.println("first one");
}
}
public class newer extends statictest
{
public void print()
{
System.out.println("second one");
}
}
and in the main function I do
statictest temp = new newer();
newer temp2 = new newer();
temp.print();
temp2.print();
Output is :
second one
second one
But When I make these 2 methods static the output is
firstone
secondone
what happened to late binding in this case?? Can anyone explain
This is called dynamic method invocation. You can look on this JLS.
It states,
The strategy for method lookup depends on the invocation mode.
If the invocation mode is static, no target reference is needed and
overriding is not allowed. Method m of class T is the one to be
invoked.
Otherwise, an instance method is to be invoked and there is a target
reference. If the target reference is null, a NullPointerException is
thrown at this point. Otherwise, the target reference is said to refer
to a target object and will be used as the value of the keyword this
in the invoked method. The other four possibilities for the invocation
mode are then considered.
static methods can not be overridden, they remains hidden if redefined in subclasses.
Ps: they do take part in inheritance. you can access static methods, from subclass name.
It is because static methods are not polymorphic. Static methods will not be Overridden.
Do search on Dynamic method dispatch
Static methods can't be overridden, that is why after making it static you are getting output like this.
static methods can not overridden.
you created object for newer class by using statictest class reference variable temp2 u hold that object by using super class reference variable. At the time of compilation compiler just checks syntax weather that method is available in the statictest class or not.if it is available it complies fine otherwise error will come.your code you declared static print method so it is availble in statictest class compilation done fine but your method is static it can't be override. now coming main method u declared
statictest temp = new newer();
now temp object is created with statictest class features only. it won't contains newer class methods or variables object is created based on the referenced class properties only it won't contains subclass properties(newer class) if super class(statictest) contains any non static values same as sub class(newer class) just it will overrides super class properties. why it overrides? because with the class it will not allow to declare same varibles or same methods
I read somewhere that in Java "constructors are not inherited".
On the other hand, I also read that if I don't explicitly call super, Java automatically calls the super class constructor with no arguments (such a constructor must exist in this case).
Isn't automatically calling the super class constructor (with no arguments) a form of inheritance?
What does "constructors are not inherited" exactly mean?
It means that you cannot create a subclass, using a constructor of the super class-if the subclass did not also declared it. An example;
class A {
A() {}
A(String s) {}
}
class B extends A {
}
Now you cannot do this:
B b = new B("testing");
It means just because your superclass has a contructor doesn't mean the subclass will automatically get the same constructor; you have to define it manually.
The default constructor is kind of an exception and kind of isn't. It's automatically defined for you, but it's not really "inherited" because it's still part of the subclass; it's not a member of the superclass.