2

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.

Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
user2469763
  • 21
  • 1
  • 3
  • No. I am supposed to use private inner class (I think) – user2469763 Jun 10 '13 at 06:24
  • @BevynQ I tried to make Date private static inner class and my original code works. So I will ask the instructor if that is acceptable. Meanwhile, any other way? Thanks – user2469763 Jun 10 '13 at 06:30
  • Your instructor needs to be clear about terminology. From the [Official Java Tutorial](http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html): Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are simply called static nested classes. Non-static nested classes are called inner classes. Generally speaking a _nested static class_ is not an _inner class_. – Ray Toal Jun 10 '13 at 06:35

2 Answers2

2

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.

Community
  • 1
  • 1
Brian
  • 17,079
  • 6
  • 43
  • 66
  • 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), ...)`? – user2469763 Jun 10 '13 at 06:52
  • Thanks Brian. I am sorry that I can not vote up your answer, yet. – user2469763 Jun 10 '13 at 11:38
0

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? :-))

Ray Toal
  • 86,166
  • 18
  • 182
  • 232
  • 1
    In theory, you're right. But, tested on ideone ([code example](http://ideone.com/HFIxVL)) and doesn't work. – Luiggi Mendoza Jun 10 '13 at 06:36
  • @LuiggiMendoza +1 Nice, thanks! I was just trying this, too. +1 for getting to it first. Makes sense because the enclosing instance doesn't exist until the person constructor is done. The whole "assignment" is very suspicious. Might be a misunderstanding? :) – Ray Toal Jun 10 '13 at 06:38
  • Here is the error message:Description Resource Path Location Type Cannot refer to 'this' nor 'super' while explicitly invoking a constructor Person.java /Wk03_Ch10_FileIO_Ch13_Interfaces/wk03_Ch10_FileIO_Ch13_Inner_Classes line 43 Java Problem – user2469763 Jun 10 '13 at 06:39
  • This can be easily solved by marking the inner class as `static`. But, if teacher doesn't want students use `static` inner class (since it is not a real inner class) then the best bet would be passing a `null` argument to the constructor and then setting the `birthDay` after calling the constructor. – Luiggi Mendoza Jun 10 '13 at 06:40
  • First help, how to do a new line in the comment? and How to make a section of code, like the original posting? Sorry, a newbie here – user2469763 Jun 10 '13 at 06:41
  • @Ray Toal: It could be my misunderstanding. Sending couple of emails already, so I guess, we just need to wait the answers. I was being super careful as my first assignment got a lot of points deducted by not following the teacher's wishes to the dot :-) My program worked and I still got a 'C'. OTH, the teacher lamented that half of the class failed. A tough grader I have in my hand ;) – user2469763 Jun 10 '13 at 06:45
  • I hope you find out what you need. I edited my answer based on Luiggi's excellent comments. I think that either (1) the static nested class is desired and the assignment was just worded in a confusing manner, or (2) you got a trick question!!, or (3) the professor assigned something without thinking through the ramifications of the problem. Hope you get this. Good question BTW. – Ray Toal Jun 10 '13 at 06:57
  • Thanks Ray also everybody else who answered. No answer from the instructor yet. Nope, not a graduate class, it is only 200 level class! That is why I was surprised by the level of the assignment and the toughness of the grading! My assignment #2 was to create a simulated world of Ant and Bug (moving, breeding, eating, starving)! – user2469763 Jun 10 '13 at 11:48