In the following example if I make a constructor of a class called example like so:
public class Example{
public Example(){
this.super();
}
}
The above will not work because javac Example.java informs about following compilation error:
Example.java:3: error: illegal qualifier; Object is not an inner class
this.super();
^
1 error
But shouldn't it work as instead of implicitly stating this by using super(), we are explicitly stating it by using this?
Although invoking a superclass constructor by calling super(args) looks like it’s a regular method call, that syntax is actually different from a typical method call and isn’t subject to the same rules. For example:
You can only use super(args) in a constructor.
You can only use super(args) as the first line of a constructor.
In that sense, it probably helps to think of this not as a method call, but simply as a way of telling Java what you want to do to initialize the superclass.
Because this isn’t a typical method call, the rules for regular method calls don’t apply to it. As a result, you can’t prefix it with this. to make the receiver object explicit. There’s no fundamental reason why the Java language designers couldn’t have made this syntax legal; they just chose not to do so.
The JLS, Section 8.8.7.1, controls the specifications for explicit constructor invocations. It is possible in the grammar to specify this.super().
Primary . [TypeArguments] super ( [ArgumentList] ) ;
And a "Primary" expression can be this. Therefore, this.super() is a legal expression according to the grammar of the Java language, but that's not enough. It's not legal according to the semantics of such an expression.
Qualified superclass constructor invocations begin with a Primary expression or an ExpressionName. They allow a subclass constructor to explicitly specify the newly created object's immediately enclosing instance with respect to the direct superclass (§8.1.3). This may be necessary when the superclass is an inner class.
The semantics indicate that here, this is attempting to indicate an enclosing instance, not the current object instance. The compiler error you get isn't the clearest, but here, this is attempting to reference the enclosing class of the superclass, but Object does not have an enclosing class.
public class J {
public J() {
this.super();
}
}
J.java:17: error: illegal qualifier; Object is not an inner class
this.super();
^
1 error
Let's attempt to use this as an enclosing instance. Class J has an inner class K, and Foo attempts to subclass J.K.
class J {
public J() { }
public class K {}
}
class Foo extends J.K {
public Foo() {
this.super();
}
}
The error now is:
J.java:21: error: cannot reference this before supertype constructor has been called
this.super();
^
1 error
I can only get it to work with a primary expression other than this.
class Foo extends J.K {
public Foo() {
new J().super();
}
}
A semantics error, not a grammar error, prevents you from using this.super().
In Java classes are initialized top down. So if you make a class that extends object, it calls object's constructor then it calls your class's.
This is also the reason you can't initialize any fields before calling super, your class hasn't been created yet. this doesn't refer to anything as the template of the object hasn't been constructed.
https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.5
More information
Related
In the following code, I noticed that I can call getWorld() without a reference to the HelloWorld object? But doesn't the implicit 'this' keyword now refer to the inner anonymous class? If so, why I am able to call getWorld()?
public class HelloWorld {
public void getWorld() {
this.setListener(new MyListenerInterface(){
#Override
public void innerMethod() {
getWorld();
}
});
}
}
Ignore the recursion in the code.
The answer is in Section 15.12 of the JLS.
If it is a simple name, that is, just an Identifier, then the name of the method is the Identifier.
If the Identifier appears within the scope of a visible method declaration with that name (§6.3, §6.4.1), then:
If there is an enclosing type declaration of which that method is a member, let T be the innermost such type declaration. The class or interface to search is T.
By using just the simple name of the method, the resolution of which method to call looks up through the enclosing methods until it finds one which has a method with that name, and then attempts to use that method (or methods) to find an exact match.
This is different than the case where you use this.getWorld(), since this refers unambiguously to the inner class instance. That results in a different type of resolution (the Typename . Identifier section of the specification, just below the linked quote), which does not look at the enclosing outer class.
An interesting consequence of this is that you can cause the code to stop compiling by adding a method to the inner class which has the same name, but a different arity. Since it only is searching for the name itself when it tries to determine the class instance to resolve it on, it will try to use the inner class, but not find an exact matching method.
So this:
public class HelloWorld {
public void getWorld() {
this.setListener(new MyListenerInterface(){
#Override
public void innerMethod() {
getWorld();
}
void getWorld(int i){}
});
}
}
would not compile. Method name resolution will discover getWorld in the inner class, and so will stop searching up the hierarchy. But when it tries to do arity resolution, it will see that none of the (one) getWorld methods in the class match, and will fail.
TL;DR - using just the simple name is note quite the same as using this.method(), even though it generally evaluates to the same thing. The language specification has specific rules for handling that case, which can allow it to look in any enclosing instance to find a matching method.
Calling getWorld you get outside the anonymous class and are back at the top level class, so this refers to the object that is creating the anonymous one.
It is like calling a method from other class, this there refers to a different object (not to the caller, but to the callee).
This question already has answers here:
Why do this() and super() have to be the first statement in a constructor?
(22 answers)
Closed 6 years ago.
Why Java compiler (javac) sometimes is stupid, sure i know that Java compiler is just a program, but sometimes is designed as stupid (sometimes not), also i'm a fan of Java :)
class Child extends Base {
Child() {
int i = 5; // any statement here.
super(i);
}
}
class Base{
Base(int i) {
}
}
here compiler, claims that the first statements should be a call to the constructor of the inherited class, but if we wrap this first statement inside a static method, it works fine!!!!!!
class Child extends Base {
Child() {
super(foo()); // works fine!!!!
}
static int foo(){
return 5;
}
}
This works fine!!!!!!!, another killer example :
Child() {
try{
super(5);
}catch(Exception e){}
}
try catch is language feature!!!
i know that the compiler oblige us to call the super type constructor, because it should initialise the inherited object before the self object (Generaly Java inheritence is released by object chaining), but i think the compiler should be a little smart, it should let us manipulate the code while we're not touching the object before calling its super constructor, so that MUST work :
Child() {
int i = 5; // this MUST BE acceptable since we didn't touch
// any current object or inherited field or we didn't
// call any method on it.
super(i);
}
but this shouldn't work :
class Child extends Base {
private int i;
Child() {
i = 6; // this shouldn't work (its clear why).
super();
}
}
I just wanted to understand why this is not implemented especially when i see Java can catch unreachable code (a smart feature)???, so for more than 20 years, Java doesn't provide such a BASIC feature, because usually this one sometimes makes code more ugly, sometimes we have to make stupid static methods to avoid this, other time, we just call the super constructor (for javac shut up) then we reinitialize the inherited fields!!!!!!!!
Althought, i don't think, this is a problem of the JVM and bytecode, i think this is can acheive only in javac :)
I really love Java, but this one makes me so angry, i forget to suggest this for the next release (Java 9), i hope it will be included in Java 10, we wait 3 years more, better than not having it at all :'(
I think this may be primarily opinion based.
The way I see it, keeping things simple is sometimes the smarter way to design.
To have the compiler accept some code that the compiler currently doesn't accept, that would (at a minimum) require more complexity. The compiler would have the additional burden of determining which constructs could be allowed, and which constructs wouldn't. The compiler is plenty complex enough. Why add more unnecessary complexity. Especially if the additional complexity doesn't solve, or help us solve, a particular problem.
A requirement that the constructor in the superclass runs before any code in the constructor of the subclass keeps things simpler.
Declaring a static method, as shown in an OP example, doesn't violate the rule about running the constructor in the superclass. A static method is never instantiated. It's part of the class definition, it's not part of an instance of a class.
I think making the compiler smarter (to handle Java code proposed by OP) would not be a smart decision at all.
Q: What real problem does the proposed change to the compiler solve?
Q: And what problems would it potentially create?
Sometimes the choice to avoid additional complexity (and potentially creating more problems) is the smarter choice.
Both answers are totally correct. I am afraid the OP wants to know the reason behind this behavior.
each object does have a constructor
each object inherits from Object - so everything is having a parent object
at each constructor, the inherited parent is called - implicitly (if you don't call super) or explicitly - if you do call super
before you can do anything with your object, it has to be ensured that the inherited object is getting properly initialized.
This is the reason why call to super() has to be the very first in your constructor, and you can not do any operations before calling super().
int i = 5;
does not work, since you could craft a more tricky initialization:
int i=someMethodCall();
which clearly might use any non-initialized inherited field if it would be allowed.
static int foo(){
return 5;
}
works, since it is a static method - which does not depend on the fields of the object instance (and can not see the fields either) - so you can call this before super();
Child() {
try{
super(5);
}catch(Exception e){}
}
does not work, since it is not guaranteed that super() will be called any more. Try..catch would allow super(5) to throw an exception (and not initialize inherited fields), while catching and ignoring this exception would yield a Child object which inherited fields are not initialized at all.
Although the behavior might sound stupid - actually it totally makes sense, no?
If a constructor body does not begin with an explicit constructor invocation and the constructor being declared is not part of the primordial class Object, then the constructor body implicitly begins with a superclass constructor invocation "super();", an invocation of the constructor of its direct superclass that takes no arguments.
From the Java specs, here.
They are actively enforcing this.
Here are the parse rules:
ConstructorBody:
{ [ExplicitConstructorInvocation] [BlockStatements] }
ExplicitConstructorInvocation:
[TypeArguments] this ( [ArgumentList] ) ;
[TypeArguments] super ( [ArgumentList] ) ;
ExpressionName . [TypeArguments] super ( [ArgumentList] ) ;
Primary . [TypeArguments] super ( [ArgumentList] ) ;
TypeArguments:
< TypeArgumentList >
ArgumentList:
Expression {, Expression}
Maybe this will give a bit of an insight. Regardless of what you do, the constructor of the super class will always be invoked first.
class Parent {
Parent(int x) {
System.out.println("Parent");
}
}
class Child extends Parent {
Child () {
super(foo());
System.out.println("Child");
}
public static void main(String[] args) {
Child a = new Child(); // prints Parent\nChild
Parent b = new Child(); // prints Parent\nChild
}
static int foo() {
return 5;
}
}
As you can see, regardless of what you do, the Parent constructor is always getting called first. This ensures that if you call any methods on the Parent class in your constructor, the Parent class has already been set up correctly. The fact that this property is strictly enforced is shown by the compiler error if I try to move the System.out.println("Child") line before the call to super().
From [JLS 8.8.7.1],
Let C be the class being instantiated, and let S be the direct
superclass of C.
It is a compile-time error if S is not accessible (§6.6).
If a superclass constructor invocation statement is qualified, then:
If S is not an inner class, or if the declaration of S occurs in a static context, then a compile-time error occurs.
Otherwise, let p be the Primary expression immediately preceding ".super". Let O be the innermost lexically enclosing class of S.
It is a compile-time error if the type of p is not O or a subclass of O, or if the type of p is not accessible (§6.6).
If a superclass constructor invocation statement is unqualified, and
if S is an inner member class, then it is a compile-time error if S is
not a member of a lexically enclosing class of C by declaration or
inheritance.
interface Problematic {
void method();
}
class Parent {
void method(int arg) { }
}
class Child extends Parent {
void test() {
new Problematic() {
#Override public void method() {
// javac error: method method in class <anonymous Problematic> cannot be applied to given types;
// required: no arguments, found: int
method(0);
Child.this.method(0); // works just fine
Child.super.method(0); // works just fine
}
};
}
}
IntelliJ IDEA also gives a warning:
Method 'method()' recurses infinitely, and can only end by throwing an exception
From the Java Language Specification,
If the form is MethodName, that is, just an Identifier, then:
If the Identifier appears in the scope of a visible method declaration
with that name (§6.3, §6.4.1), then:
If there is an enclosing type declaration of which that method is a member, let T be the innermost such type declaration. The class or
interface to search is T.
This search policy is called the "comb rule". It effectively looks for methods in a nested class's superclass hierarchy before looking
for methods in an enclosing class and its superclass hierarchy. See
§6.5.7.1 for an example.
Otherwise, the visible method declaration may be in scope due to one or more single-static-import or static-import-on-demand declarations.
There is no class or interface to search, as the method to be invoked
is determined later (§15.12.2.1).
You're invoking the method inside the anonymous inner class which is a subtype of Problematic. This makes Child its enclosing type. In your case, T is the anonymous inner class, since that is the innermost type. Because of this, the class to search for the method is T, ie. the anonymous subclass of Problematic.
In later steps of the resolution of method invocation expressions, it is decided that the method Problematic#method() takes no arguments since it declares no parameters. It is therefore not applicable and a compiler error is raised.
See https://docs.oracle.com/javase/tutorial/java/javaOO/nested.html:
Shadowing
If a declaration of a type (such as a member variable or a parameter name) in a particular scope (such as an inner class or a method definition) has the same name as another declaration in the enclosing scope, then the declaration shadows the declaration of the enclosing scope.
An overload in a child class hides the method being overloaded in the parent class. In this case, Problematic.method() overloads and hides Parent.method(int).
Explicit is always better than implicit!
method(0) is actually this.method(0) which is actually Child$1.this.method(0) and Child$1.this.method(int) does not exist in the declaration of Problematic.
This is explained in the error messages from the compiler and from the IDE.
The compiler tells you that this is exactly what it is doing, it is resolving to the most local scope and finding something and that something isn't correct and it stops looking.
You are doing this:
#Override public void method() {
// javac error: method method in class <anonymous Problematic> cannot be applied to given types;
// required: no arguments, found: int
method(0);
Child.this.method(0); // works just fine
Child.super.method(0); // works just fine
}
Which actually implicitly means the following:
#Override public void method() {
// javac error: method method in class <anonymous Problematic> cannot be applied to given types;
// required: no arguments, found: int
this.method(0); // this is implied in the above code
// and resolves to the Problematic declaration
Child.this.method(0); // will never be reached if the previous call is actually corrected.
Child.super.method(0); // will never be reached either
}
this.method(0) is invalid code and if you called this.method() it would just recurse infinitely and throw a StackOverflow error.
This is explained in the error messages from the compiler and from the IDE.
This is how the scope resolution works, there is no mystery why, read the scope resolution rules and you will see it looks for this.XXX first then broadens the scope.
If your goal is to call method(int) on Child you already know how to do that, you have to call it with Child.this.method(int), it has to be fully qualified because of the namespace scoping resolution.
I've read about class fromal parameters and the question then arises as to why the following code is ill-formed?
class A:
package org.gradle;
public class A extends B.Inner{
public A(B s){
s.super(new B()); //error
s.super(); //OK
}
}
class B:
package org.gradle;
public class B{
public class Inner{
}
}
The key part of what was said is:
The constructor of a non-private inner member class implicitly
declares, as the first formal parameter, a variable representing the
immediately enclosing instance of the class
So, I expect that besides the default constructor, we should have a constructor with the following signature:
Inner(B b);
Why not?
The "extra" parameter is effectively hidden from you - both when you declare it, and when you execute it. When you execute it, you provide a value in a different way - in your case, via s:
s.super();
That's passing s as the hidden extra argument to the B.Inner constructor. The syntax for all of this is a little weird - and I would personally try to avoid using inner classes in this sort of situation... they just get weird very quickly. I usually prefer static nested classes, and if I do need an inner class, it's almost always private. Subclassing an inner class declared in a different top-level class is an odd situation, IMO.
You are already passing the enclosing instance s (of class B) to the constructor of the inner class when you call s.super(), so there's no need to pass another instance of B.
The variable representing the immediately enclosing instance of the class is an implicit parameter, and it is passed to the constructor using a special syntax.
Let's look at the following code snippet in Java.
package trickyjava;
class A
{
public A(String s)
{
System.out.println(s);
}
}
final class B extends A
{
public B()
{
super(method()); // Calling the following method first.
}
private static String method()
{
return "method invoked";
}
}
final public class Main
{
public static void main(String[] args)
{
B b = new B();
}
}
By convention, the super() constructor in Java must be the first statement in the relevant constructor body. In the above code, we are calling the static method in the super() constructor parameter list itself super(method());.
It means that in the call to super in the constructor B(), a method is being
called BEFORE the call to super is made! This should be forbidden by the compiler but it works nice. This is somewhat equivalent to the following statements.
String s = method();
super(s);
However, it's illegal causing a compile-time error indicating that "call to super must be first statement in constructor". Why? and why it's equivalent super(method()); is valid and the compiler doesn't complain any more?
The key thing here is the static modifier. Static methods are tied to the class, instance methods (normal methods) are tied to an object (class instance). The constructor initializes an object from a class, therefore the class must already have been fully loaded. It is therefore no problem to call a static method as part of the constructor.
The sequence of events to load a class and create an object is like this:
load class
initialize static variables
create object
initialize object <-- with constructor
object is now ready for use
(simplified*)
By the time the object constructor is called, the static methods and variables are available.
Think of the class and its static members as a blueprint for the objects of that class. You can only create the objects when the blueprint is already there.
The constructor is also called the initializer. If you throw an exception from a constructor and print the stack trace, you'll notice it's called <init> in the stack frame. Instance methods can only be called after an object has been constructed. It is not possible to use an instance method as the parameter for the super(...) call in your constructor.
If you create multiple objects of the same class, steps 1 and 2 happen only once.
(*static initializers and instance initializers left out for clarity)
Yep, checking the JVM spec (though admittedly an old one):
In the instance init method, no reference to "this" (including the implicit reference of a return) may occur before a call to either another init method in the same class or an init method in the superclass has occurred.
This is really the only real restriction, so far as I can see.
The aim of requiring the super constructor to be invoked first is to ensure that the "super object" is fully initialized before it is used (It falls short of actually enforcing this because the super constructor can leak this, but that's another matter).
Calling a non-static method on this would allow the method to see uninitialized fields and is therefore forbidden. A static method can only see these fields if it is passed this as argument. Since accessing this and super is illegal in super constructor invocation expressions, and the call to super happens before the declaration of any variables that might point to this, allowing calls to static methods in super constructor invocation expressions is safe.
It is also useful, because it allows to compute the arguments to the super constructor in an arbitrarily complex manner. If calls to static methods weren't allowed, it would be impossible to use control flow statements in such a computation. Something as simple as:
class Sub extends Super {
Sub(Integer... ints) {
super(Arrays.asList(ints));
}
}
would be impossible.
This is one situation where the java syntax hides what's really going on, and C# makes it a bit clearer.
In C# your B would look like
class B : A {
public B() : base(method()) {
}
private static String method() {
return "method invoker";
}
}
Although the java syntax places super(method) within the constructor it's not really called there: All the parent initialization is run before your subclass constructor. The C# code shows this a little more clearly; by placing super(method()) at the first line of the java constructor you're simply telling java to use the parameterized constructor of the super class rather than the parameterless version; this way you can pass variables to the parent constructor and they'll be used in the initialization of the parent level fields before your child's constructor code runs.
The reason that super(method()) is valid (as the first line in a java constructor) is because method() is being loaded with the static elements--before the non-static ones, including the constructors--which allows it to be called not only before B(), but before A(String) as well. By saying
public B() {
String s = method();
super(s);
}
you're telling the java compiler to initialize the super object with the default constructor (because the call to super() isn't the first line) and you're ready to initialize the subclass, but the compiler then becomes confused when it sees that you're trying to initialize with super(String) after super() has already run.
A call to super is a must in java to allow the parent to get initalized before anything with child class starts.
In the case above, if java allows String s= method(); before the call to super, it opens up flood gate of things that can be done before a call to super. that would risk so many things, essentially that allows a half baked class to be used. Which is rightly not allowed. It would allow things like object state (some of which may belong to the parent) being modified before it was properly created.
In case of super(method()); call, we still adhere to the policy of completing parent initialization before child. and we can use a static member only, and static member of child classes are available before any child objects are created anyways. so the method is avilable and can be called.
OK..i think, this one could be relevant that, if we are calling some member with Super, then it first try to invoke in super class and if it doesn't find same one then it'll try to invoke the same in subclass.
PS: correct me if i'm wrong