0

Is the following accepted in Java ? I know it compiles, but is it accepted in Java, or should I change the code to something else ?

Thanks !

public class Line extends Shape {

    public Line(int x1, int x2, int y1, int y2, Color myColor) {
        super(x1, x2, y1, y2, myColor);

        Point p1 = new Point(this.getX1(),this.getY1());
        Point p2 = new Point(this.getX2(),this.getY2());
    }
Batman
  • 1,244
  • 2
  • 14
  • 26
  • 1
    Why wouldn't it be "accepted"? – Oliver Charlesworth May 24 '15 at 10:20
  • 2
    What is the purpose of `p1` and `p2` (beside being garbage collected)? – Pshemo May 24 '15 at 10:20
  • p1,p2 - represents the line points...the issue is that I want to use those points in some of the object's method... – Batman May 24 '15 at 10:21
  • but that will not be available outside constructor,so there will be no use of that.Instead if that you should declare p1 and p2 as instance variable of class. – Krutik Jayswal May 24 '15 at 10:21
  • I am asking how are you planning to use them? Where? When? – Pshemo May 24 '15 at 10:21
  • I want to use those points in some of the object's method to ease calculations... – Batman May 24 '15 at 10:22
  • Have you tried using them in these methods? Did it work? – Pshemo May 24 '15 at 10:23
  • yap, it recognized them inside Line object's methods I built.. but is it accepted in Java to act in this way or should I create them outside the constructor, and outside methods ?? – Batman May 24 '15 at 10:25
  • 1
    If it works for you then you posted wrong example because in your code `p1` and `p2` are *local* variables of constructor which means that they are not available outside (for instance in other methods). Please correct your example. – Pshemo May 24 '15 at 10:26
  • Anyway I suspect that you wanted to ask this question: [Should I initialize variable within constructor or outside constructor](http://stackoverflow.com/questions/3918578/should-i-initialize-variable-within-constructor-or-outside-constructor) (which is not what your current question is about) – Pshemo May 24 '15 at 10:35

2 Answers2

4

If you want to use the variable p1 and p2 outside the constructor you have to declare it to the class level variable like this

Point p1;
Point p2;
public Line(int x1, int x2, int y1, int y2, Color myColor) {
    super(x1, x2, y1, y2, myColor);

    p1 = new Point(this.getX1(),this.getY1());
    p2 = new Point(this.getX2(),this.getY2());
}

Otherwise its fine.

Meetesh
  • 172
  • 2
  • 13
3

p1,p2 - represents the line points...the issue is that I want to use those points in some of the object's method.

Then you must declare them outside the constructor; if you declare them inside the constructor, they're local variables. Like local variables in any function, they aren't in scope outside the function. ("function" = "constructor or method")

You can initialize them inside the constructor, but you must declare them outside:

public class Line extends Shape {
    private Point p1; // You may or may not want the `private`, it...
    private Point p2; // ...depends what you're going to do with them

    public Line(int x1, int x2, int y1, int y2, Color myColor) {
        super(x1, x2, y1, y2, myColor);

        this.p1 = new Point(this.getX1(),this.getY1());
        this.p2 = new Point(this.getX2(),this.getY2());
    }

    // ...
}

Note: Accessing instance fields such as the above can be done with or without the this. in front. (E.g., this.p1 = new ... and p1 = new... are both valid.) I always use this. so that it's easy to tell, looking at the code, whether I'm using an instance field or a local variable; my IDE can also help me out more with autocomplete. I think I'm probably in the minority there, though.


Regarding your question about whether to initialize outside the constructor or inside, let's talk about when the initialization you write outside the constructor is actually done, which can be non-obvious:

class A { 
    int x;
    int y;

    public A(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return this.x;
    }

    public int getY() {
        return this.y;
    }
}
class B extends A {
    private ImmutablePoint p = new ImmutablePoint(this.getX(), this.getY());

    public B(int x, int y) {
        super(x, y);
        System.out.println("Hello from B");
    }

    public Point getP() {
        return this.p;
    }
}

It looks like the initialization happens before the constructor is called (to me, anyway). That isn't what happens. The bytecode the compiler produces for class B looks like this instead:

// Reconstituted version of the bytecode for our B above
class B extends A {
    private ImmutablePoint p;

    public B(int x, int y) {
        super(x, y);
        this.p = new ImmutablePoint(this.getX(), this.getY()); // <== Inserted
        System.out.println("Hello from B");
    }

    public Point getP() {
        return this.p;
    }
}

Note how it inserted your initialization logic into the constructor.

Here are some reasons for writing your initialization in the constructor rather than as part of the declaration:

  1. If you needed to use information that was only available inside the constructor (an argument that doesn't become part of the object state), you have no choice, you have to do the initialization in the constructor. For instance, there's no way for A to initialize its x field except with its x argument.

  2. If you needed to do initialization differently in different constructors. Again, you'd have no choice but to do the initialization in the constructor rather than with the declaration.

  3. (This is subjective.) Clarity. Since the initialization code is run after the call to super and before anything else in your constructor, writing it there in the source code can aid clarity.

There's an argument for writing your initialization outside the constructor, with the declaration:

  1. If you have multiple constructors and the initialization logic is identical for all of them, writing it with the declaration lets you write it only once and reuse it. (You could also do that by calling a method within your constructor, but some people frown on that.) There's an alternative do this, however, which I'll flag up below.

Let's look at that in the context of B:

class B extends A {
    private ImmutablePoint p = new ImmutablePoint(this.getX(), this.getY());

    public B(int x, int y) {
        super(x, y);
        System.err.println("Hello from B");
    }

    public B(int x, int y, String msg) {
        super(x, y);
        System.out.println(msg);
    }

    public Point getP() {
        return this.p;
    }
}

Now we have two constructors that do slightly different things (System.err vs. System.out, using an argument or using a default message.) The bytecode the compiler produces actually does this:

// Reconstituted version of the bytecode for our B above
class B extends A {
    private ImmutablePoint p;

    public B(int x, int y) {
        super(x, y);
        this.p = new ImmutablePoint(this.getX(), this.getY()); // <== Inserted
        System.err.println("Hello from B");
    }

    public B(int x, int y, String msg) {
        super(x, y);
        this.p = new ImmutablePoint(this.getX(), this.getY()); // <== Inserted
        System.out.println(msg);
    }

    public Point getP() {
        return this.p;
    }
}

So there's arguably a benefit to writing that initialization in one place at the declaration.

It's a style choice; an alternative to it is to use a common denominator constructor:

class B extends A {
    private ImmutablePoint p;

    private B(int x, int y) {
        super(x, y);
        this.p = new ImmutablePoint(this.getX(), this.getY());
    }

    public B(int x, int y) {
        this(x, y);
        System.err.println("Hello from B");
    }

    public B(int x, int y, String msg) {
        this(x, y);
        System.out.println(msg);
    }

    public Point getP() {
        return this.p;
    }
}

Finally: Java also has initializer blocks, which are initializer code written outside of any constructor, but in a very method-like way:

class B extends A {
    private ImmutablePoint p;

    // Instance initializer block:
    {
        this.p = new ImmutablePoint(this.getX(), this.getY());
    }

    public B(int x, int y) {
        super(x, y);
        System.err.println("Hello from B");
    }

    public B(int x, int y, String msg) {
        super(x, y);
        System.out.println(msg);
    }

    public Point getP() {
        return this.p;
    }
}

You can have limited logic within initializer blocks.

So you have all sorts of options. Other than when you're required to initialize in a constructor (#1 or #2 on my earlier list), it's a style choice.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Is there a benefit initialize the object in the constructor, rather than declare+initialize them, all outside the constructor ? thx, – Batman May 24 '15 at 10:30
  • So declaring "private Point p1 = new Point(this.getX1(),this.getY1());" outside constructor as a class object will not be accepted in java ? – Batman May 24 '15 at 10:39
  • @Batman: Sorry, I misread! There are (at least) three reasons for initializing inside the constructor: 1. If you were using information during the initialization that was an argument to the constructor, rather than being something set by the super's constructor. 2. If you wanted to initialize things *differently* in different constructors. 3. (Subjective) Clarity. Although you *can* do `Point p1 = new Point(this.getX1()...)` outside, I'd say it's clearer to do it in the constructor after the `super` call to make it obvious it depends on the `super` call. *(cont'd)* – T.J. Crowder May 24 '15 at 10:40
  • *(continuing)* An argument for initialize *outside* the constructor is that if the initialization logic is the same for all of your constructors, you only write it in one place. – T.J. Crowder May 24 '15 at 10:40
  • @Batman: *"So declaring "private Point p1 = new Point(this.getX1(),this.getY1());" outside constructor as a class object will not be accepted in java ?"* It *is* valid, sorry, I was a bit misleading before. It's effectively inserted after the `super` call in your constructor, before the first line after the `super` call. Which is the basis of my #3 above. :-) – T.J. Crowder May 24 '15 at 10:41
  • So the main reason here for split the pointer and the initialization between the constructor and class is the x1,x2, in my case, that are affected the p's initialization in my case ? thx – Batman May 24 '15 at 10:43
  • @Batman: With this specific example, you could do either, because you're using the accessor methods in the initialization. If you had other constructors and the initialization needed to be the same for all, there's an argument for doing it outside. Otherwise, *subjectively*, as a matter of opinion, I'd go with initializing inside the constructor after the `super` call (since that's when it will happen anyway). – T.J. Crowder May 24 '15 at 10:45
  • Now I know better about java's constructor and better understand the initialization issues in Java, Thanks :) – Batman May 24 '15 at 10:46
  • @Batman: :-) Glad that helped. I added most of our comments discussion into the answer, with examples and further explanation. – T.J. Crowder May 24 '15 at 11:06