Inner class and this() constructor - java

I have 2 classes: Date and Person
Person has two attributes of Date class
Case 1
Date class is separate class from Person class. I have this piece of code working properly:
private String name;
private Date born;
private Date died; // null indicates still alive.
public Person(String initialName, int birthMonth, int birthDay,
int birthYear) {
// requirement from the instructor:
// **implement using the this() constructor**
this(initialName, new Date(birthMonth, birthDay, birthYear), null);
}
Case 2: Inner class (an assignment requirement)
I put the Date as the private inner class of Person
Now the above constructor code does not work anymore.
Here is the error message:
Description Resource Path Location Type
No enclosing instance of type Person is available due to some intermediate constructor invocation Person.java /Wk03_Ch10_FileIO_Ch13_Interfaces/wk03_Ch10_FileIO_Ch13_Inner_Classes line 43 Java Problem`
How do I solve the problem? I can do this:
Date dt = new Date(birthMonth, birthDay, birthYear);
Unfortunately this() has to be the first line in the constructor
Another work around is
public Person(String initialName, int birthMonth, int birthDay,
int birthYear) {
// implement using the this() constructor
this.name = initialName;
this.born = new Date(birthMonth, birthDay, birthYear);
this.died = null;
}
However the last piece of code does not satisfy my instructor requirement of using this() method inside the constructor.

You can't create inner member (non-static) classes within a call to another constructor. From JLS §8.8.7.1:
An explicit constructor invocation statement in a constructor body (sic: the call to this()) may not refer to any instance variables or instance methods or inner classes declared in this class or any superclass, or use this or super in any expression; otherwise, a compile-time error occurs.
The reason is that non-static inner classes may require access to the class while it's being constructed. For example:
public class OuterClass {
private String name;
private InnerClass inner;
public OuterClass(String name, InnerClass inner) {
this.name = name;
this.inner = inner;
}
public OuterClass(String name) {
this(name, new InnerClass()); // Will not compile
}
public class InnerClass {
public InnerClass() {
// Access to name is allowed since this inner class isn't static
System.out.println(name);
}
}
}
The major problem here is that when we construct the non-static inner class, it can access non-static members from its enclosing instance (the OuterClass, but the enclosing OuterClass hasn't yet made its call to super() and is therefore considered unsafe to access. In fact, if that code was allowed to compile, it would print null due to constructor invocation order. In short, the InnerClass would be created before the call to this.name = name, a similar concept to the information presented in this question.
The solution is to make InnerClass a static inner class and pass the name to it directly:
public class OuterClass {
private String name;
private InnerClass inner;
public OuterClass(String name, InnerClass inner) {
this.name = name;
this.inner = inner;
}
public OuterClass(String name) {
this(name, new InnerClass(name));
}
public static class InnerClass {
public InnerClass(String name) {
System.out.println(name);
}
}
}
InnerClass cannot access name from the OuterClass once it's declared static, so we have to pass it explicitly on construction now, but this is better since the initial code would have been broken anyway.
Edit:
Per your question:
What confused me is that I can create an object of a Date type in the Person's constructor, as long as it is not inside the this() method. I can do this: Date dt = new Date(birthMonth, birthDay, birthYear); What is the difference between the above and this(...., new Date(birthMonth, birthDay, birthYear), ...)?
The difference is that in the call outside of this(), all the calls to super() have taken place, they take place as part of this() because of implicit calls to super(), so the object has reached a point where it is considered okay to be accessed. Your Date instance can't access the Person class because there is no context for the Person and its fields yet, so the compiler doesn't allow it.
In short, once this() has been called, then at least the calls to super() have happened, which is the driving force behind this constraint, and also why overridable method calls are discouraged. If a method is overridden by a subclass and then called in the superclass' constructor, fields from the subclass can be accessed before the subclass has even been initialized, even resulting in null being returned for final fields. Basically, it's all about protecting yourself from accessing your class before a call to super() has been invoked.

While I would never create a Date class inside of a Person class (sounds like a bad idea from an application-modeling perspective!), you seem to have said that it is a requirement in some assignment.
If you are set up like this:
public class Person {
...
class Date {
...
}
}
Then inside methods of Person, you will need to invoke the Date constructor with:
this.new Date(...)
That is the syntax Java uses. You need an enclosing instance of type Person on which to create objects of the inner class. The thing about inner classes (whether they are member, local, or anonymous) is that each instance exists bound to an instance of the outer class. So if I had a person instance p, I could say:
p.new Date(...)
The big problem here though is that you cannot create dates in the Person constructor that use the about-to-be-created person! For example, this fails:
public Person() {
this(this.new Date());
}
because the value of this isn't ready for use in this way yet (although interestingly, you can sometimes use this inside constructors in other cases, such as storing it in arrays, for example).
Like you realized, making Date a static nested class is fine, because instances of static nested classes are not tied to any instances of the enclosing class, so this is the best solution. If you really have to have an inner class, you're not going to be able to pass a new date as an "argument" of a this() expression and have it bound to the person you are creating! Maybe that is the point of the assignment (is this a graduate class? :-))

Related

Why do method() and super.method() refer to different things in an anonymous subclass?

I was solving some exercises to understand better how inner classes in java work. I found one quite interesting exercise. The condition of the exercise is to make printName() print "sout" instead of "main" with minimum changes. There is its code:
public class Solution {
private String name;
Solution(String name) {
this.name = name;
}
private String getName() {
return name;
}
private void sout() {
new Solution("sout") {
void printName() {
System.out.println(getName());
// the line above is an equivalent to:
// System.out.println(Solution.this.getName);
}
}.printName();
}
public static void main(String[] args) {
new Solution("main").sout();
}
}
We've got an amusing situation - the two classes have is-A and has-A connections.
It means that the anonymous inner class extends the outer class and also objects of the inner class have references to the objects of the outer class.
If you run the code above, "main" will be printed. The child cannot invoke getName() of the parent through inheritance. But the child being inner class uses reference to parent(outer class) to access the method.
The simplest way to solve this task is to change the access modifier of getName() from private to anything else. So the child is able to use getName() through inheritance and thanks to late binding "sout" will be printed.
The another way to solve this task is to use super.getName().
private void sout() {
new Solution("sout") {
void printName() {
System.out.println(super.getName());
}
}.printName();
}
And I cannot understand how it works. Can someone help me to understand this problem?
Thank you for trying)
The Java Language Specification (JLS), in the context of the compiler resolving a method invocation expression, states
If the form is super . [TypeArguments] Identifier, then the class to
search is the superclass of the class whose declaration contains the
method invocation.
The class whose declaration contains the method invocation, in this case, is the anonymous Solution subclass and its superclass is Solution. The JLS, in the context of determining which instance will be used to invoke the method, then goes on to say
If the form is super . [TypeArguments] Identifier, then the target
reference is the value of this.
this, in this case, refers to the instance of the anonymous Solution subclass. That instance's name field was initialized with the value "sout" as so that is what getName() returns.
In the original sample,
new Solution("sout") {
void printName() {
System.out.println(getName());
}
}.printName();
the getName() method invocation is unqualified and therefore a different rule applies. That is
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.
T, here, is the Solution class, since it is the innermost enclosing type of the anonymous Solution subclass and getName() is its member.
Then, the JLS states
Otherwise, let T be the enclosing type declaration of which the method
is a member, and let n be an integer such that T is the n'th lexically
enclosing type declaration of the class whose declaration immediately
contains the method invocation. The target reference is the n'th
lexically enclosing instance of this.
Again, T is Solution, the 1st lexically enclosing type since the class whose declaration immediately contains the method invocation is the anonymous Solution subclass. this is the anonymous Solution subclass instance. The target reference is therefore the 1st lexically enclosing instance of this, ie. the Solution instance, whose name field was initialized with the value "main". That's why the original code prints "main".
The behavior may seem counter-intuitive but it becomes clear with a bit of refactoring.
So, the sout() method can actually be rewritten as
private void sout() {
new Solution("sout") {
void printName() {
String name = getName();
System.out.println(name);
}
}.printName();
}
public static void main(String[] args) {
Solution mainSolution = new Solution("main");
mainSolution.sout();
}
Calling the sout() method of the mainSolution object, creates a child Solution object that has an additional printName() method, which calls
getName();
which only declared in the parent mainSolution object.
If getName() is declared as private, it is not overriden but it is still accessible from the inner class, so getName() refers to the name of the mainSolution, i.e. to main.
If getName() has no modifier, or is declared as protected or public, then it is inherited (overriden) and refers to the child Solution object's name, i.e. to sout, hence "sout" will be printed.
By replacing getName() in sout() with
Solution.this.getName()
the string "main" will be printed in both scenarios.
By replacing it with either of
this.getName()
super.getName()
if the getName() method is declared as private a compilation error will occur, otherwise the string "sout" will be printed.

Using Java reflection to modify nesting class's fields from inner class

I'm experiencing odd behavior while debugging my Java reflection homework in Eclipse.
I have 2 classes defined:
public class OuterClass{
protected Person person;
public void aPerson(Integer age) {
person = new Person(age);
}
}
public class OuterDerivedClass extends OuterClass {
public class InnerClass extends OuterClass {
public void aPerson(Integer age) {
person = new Person(age);
}
}
}
At some point in my program, I instantiated an InnerClass instance like so (using reflection as my program is supposed to work on any class. Nested classes could be either static/non-static and public/private/protected):
Class<?> outer_class = outer_instance.getClass();
Constructor<?> ctor = inner_class.getDeclaredConstructor(outer_class);
Object inner_instance = ctor.newInstance(outer_instance);
Later on I invoked the OuterDerivedClass$InnerClass.aPerson method:
method.invoke(inner_instance, parameter);
For now, it seemed like all went OK: The inner_instance was created successfully, the correct aPerson method (the one in InnerClass) was invoked. BUT, later on my program, as I tried to access the person field from outer_instance, I figured out it was still NULL (though it was created in aPerson method).
While debugging in Eclipse, I found out this:
(PersonStoryDerivedTest == OuterDerivedClass in my question).
The id of this$0 is the same as the one I sent to the constructor (outer_instance). I couldn't figure out what is the second person field (the one that apparently was created by invoking aPerson). Isn't this field supposed to exist only in the nesting class? My intention was that the invocation of OuterDerivedClass$InnerClass.aPerson would change the person field in outer_instance (I thought this is the reason why it was sent to the Constructor in the first place).
Can someone please clarify these issues? Thanks!
I couldn't figure out what is the second person field
Well, since
public class InnerClass extends OuterClass {
// ^^^^^^^^^^^^^^^^^^
your InnerClass also inherits its own person field which is set by
public void aPerson(Integer age) {
person = new Person(age);
}
because person refers to this.person and this inside this method represents instance of class where method was implemented (in InnerClass).
If you want your method to initialize OuterClass#person field you need to refer to it via OuterDerivedClass.this.person, but this will leave uninitialized person field of InnerClass.
Other (simpler) option is not extending OuterClass from InnerClass:
public class InnerClass{

Use of "this" operator in Java

Is there any other use of this keyword other than accessing member variable having the same name as local variable
this.x = x
Is there any other situation where it make sense to use this keyword.
One other use of this keyword is in constructor chaining, for example:
class Person {
private String name;
private int age;
public Person() {
//Invoking another constructor
this("John", 35);
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
You can pass the current object as a parameter to another method.
Below points have been taken from Java docs
The most common reason for using the this keyword is because a field is shadowed by a method or constructor parameter.
From within a constructor, you can also use the this keyword to call another constructor in the same class.
this represents the current instance inside the instance.
It is useful for:
identifying instance variables from locals (including parameters)
it can be used by itself to simply refer to member variables and methods, invoke other constructor overloads.
refer to the instance.
Some examples of applicable uses (not exhaustive):
class myClass
{
private int myVar;
public myClass() {
this(42); // invoke parameterized constructor of current instance
}
public myClass(int myVar) {
this.myVar = myVar; // disambiguate
}
public void another() {
this.second(); // used "just because"
}
private void second() {
System.out.println("whatever");
}
}
You can reference a field or call a method of an enclosing class
public class Examples {
public class ExamplesInner {
private int x;
public ExamplesInner() {
x = 3; // refers to ExamplesInner.x
Examples.this.x = 3; // refers to Examples.x
}
}
private int x;
}
For full usage, read the java language specification
The keyword this may be used only in the body of an instance method,
instance initializer, or constructor, or in the initializer of an
instance variable of a class. If it appears anywhere else, a
compile-time error occurs.
When used as a primary expression, the keyword this denotes a value
that is a reference to the object for which the instance method was
invoked (§15.12), or to the object being constructed.
The type of this is the class C within which the keyword this occurs.
At run time, the class of the actual object referred to may be the
class C or any subclass of C.
The keyword this is also used in a special explicit constructor
invocation statement, which can appear at the beginning of a
constructor body (§8.8.7).
this keyword can be used to refer current class instance variable.
this() can be used to invoke current class constructor.
this keyword
can be used to invoke current class method (implicitly)
this can be
passed as an argument in the method call.
this can be passed as
argument in the constructor call.
this keyword can also be used to
return the current class instance.
find examples from this:
http://javarevisited.blogspot.in/2012/01/this-keyword-java-example-tutorial.html
The this operator is used as reference to currently executing object. It is concerned with objects, and hence cannot be and should not be used with static references, where classes are used instead of objects.
The this operator can be used to invoke the constructor like this()
The this operator also avoids naming ambiguities and can be used to pass the current object as reference to functions
this lets you disambiguate between the private member foo and the parameter foo passed into the constructor: EX
class bar
{
private int foo;
public Foo(int foo)
{
this.foo =foo;
}
}

java inheritance

When I change this code into Member m1 = new Member (); It works perfectly. Why it does not work for Super class reference? Please can someone explain?
public class Family {
String Surname = "Richard";
String Address = "No:10, High Street,Colombo";
}
public class Member extends Family{
String Name;
int age;
public void Details() {
System.out.println("full Name ="+ Name +" "+ Surname);
System.out.println("Age =" +age);
System.out.println("Address =" + Address);
}
public static void main(String[] args) {
Member m1 = new Family ();
m1.Name="Anne";
m1.age=24;
m1.Details();
}
You don't have a super class reference. You are having a subclass reference holding a reference to a super class object, that is simply illegal.
Secondly, you need to defined the method you have in subclass also in super class, if you want to see polymorphism into effect. You can only invoke that method on super class reference, that is also defined in super class.
So, you basically need this: -
Family m1 = new Member();
and then define the details() method(Yes, method name should start with lowercase alphabets), in your Family class.
And now, you will get another compiler error, when you try to access the fields of Member. For that, it's better to use a 2-arg constructor in Member class, and from that constructor, invoke the 2-arg super constructor (you need to do this explicitly), or 0-arg super constructor (this is implicit)
public Member(String name, int age) {
this.name = name;
this.age = age;
}
And use this constructor to create the object. It will implicitly call the 0-arg super constructor to initialize it's fields with their default value. If you want to give them a value, you can use a 4-arg constructor in Member class, passing the 2-parameters for super class fields, and then invoke the super class 2-arg constructor from there.
I think, you should better start with a good tutorial, starting with learning Inheritance in general, the access specifiers, then move ahead with polymorphism.

Do I really need to define default constructor in java?

It works fine when constructors are not defined, but gives errors if I define a parameterized constructor and not a default one and not passing any values while creating an object. I thought constructors are predefined.
Why do I need to define a default constructor if I've defined a parameterized constructor? Ain't default constructor predefined?
A default (no-argument) constructor is automatically created only when you do not define any constructor yourself.
If you need two constructors, one with arguments and one without, you need to manually define both.
While all the answers above are correct, it's a bit difficult for new-comers to wrap it in their head. I will try to answer the question anew for new-comers.
The problem that Ayush was facing was in trying to instantiate an Object for a class via a no-arg constructor. This class however has one or more parameterized constructor and this results in a compile time error.
For example, let us create a class Student with a single parameterized constructor and try to instantiate it via the no-arg constructor.
public class Student {
private String name;
private int rollNo;
public Student(String name, int rollNo) {
this.name = name;
this.rollNo = rollNo;
}
public static void main(String[] args) {
// The line below will cause a compile error.
Student s = new Student();
// Error will be "The constuctor Student() is undefined"
}
}
Woha! But when we remove the public Student(String name, int rollNo)
constructor all-together, the error is gone and the code compiles.
The reason behind this seeming anomaly lies in the fact that Java only
provides us with the default (no-arg) constructor when we do not define any
constructor for that class on our own.
For example, the following class is supplied with a default contructor:
public class Student {
private String name;
private int rollNo;
}
becomes:
public class Student {
private String name;
private int rollNo;
//Default constructor added by Java.
public Student() {
super();
}
}
In other words, the moment we define any parameterized constructor,
we must also define a no-arg constructor if we want to instantiate
the object of that class via a no-arg constructor.
Also in case of inheritance, a sub-class with no constructors; is supplied one
default constructor. This default constructor supplied by Java as above calls the super class's no-arg constructor. If it can't find one, then it will throw an error.
So yes it's always a good choice to define a no-arg/default constructor.
Ref : Oracle Java Tutorial
A no-arg constructor is automatically inserted for you, if you don't write one. This means, if you write a constructor with some parameters, it will be the only constructor you have, so you must pass some values for those parameters to create an instance of it.
This is exactly the expected behavior.
Java automatically generates a default (no arguments constructors) for classes that don't have any constructor.
If you define another constructor (with arguments), default constructor will not be generated. If you still want one, you need to define it yourself.
Whenever you class is complied, if compiler does not find any valid constructor in class (default,parametrized) only then it will substitute auto generated default constructor for your class.
You must have noticed, you can create objects without any default constructor defined in your class, there comes the concept of auto-generated default constructor.As whenever object is created,default constructor is called.
But, If you define Parametrized constructor in your class, that means you restrict the objects to have that parameters
Example:Every employee must have an id.
So,at that time, Compiler will not insert any default constructor there as there is valid constructor in a class.If you need default constructor too, you have to define by yourself.
There is also one curious case when you must define non argument constructor.
As the other wrote, if you don't specify default constructor - Java will do it for you. It's good to understand how "default generated by Java" constructor looks like.
In fact it calls constructor of the super class and this is fine.
Let's now imagine one case.
You are creating Vehicle class:
public class Vehicle {
private String name;
private String engine;
public Vehicle(String name, String engine) {
this.name = name;
this.engine = engine;
}
public String makeNoise(){
return "Noiseee";
}
}
As we can see Vehicle class has got only one defined 2 arguments constructor.
Now let's create Car class which inheritates from Vehicle class:
public class Car extends Vehicle {
#Override
public String makeNoise() {
return "Wrrrrrrr....";
} }
Maybe it looks strange but only one reason why wouldn't it compile is fact that Java cannot create default constructor for Car class which call super Vehicle class. Vehicle class doesn't have no argument constructor and it cannot be generated automatically while 2 arg constructor already exists.
I know that it's very rare case, but I found it as a interesting to know.

Categories

Resources