1

Here I have a simple class that extends a class in the ACM graphics library called GRect. Grect is a basic rectangle that can be drawn to a GCanvas (Also part of ACM). What I want to achieve is a new object that is a rectangle, but has an attached label that moves with the rectangle.

In my code, I have created a class named labeledRect that extends GRect, and contains an instance variable "rectLabel" that is of type GLabel, that is initialized in labeledRects constructor. I want to override some of GRect's methods so that when labledRect is moved, rectLabel moves with it.

My issue is that despite "rectLabel" being declared as an instance variable, and initialized in the constructor, it becomes NULL in the overridden method "setLocation". I have also attempted to initialize "rectLabel" at declaration, but the same problem occurs.

import acm.graphics.*;

public class labeledRect extends GRect {

    //GLabel declared as an instance variable
    public GLabel rectLabel;

    public labeledRect(double x, double y, double width, double height, String theLabel) {
        //Call GRect constructor
        super(x, y, width, height);

        //Label initialized. 
        //Location setting and adding to the canvas works fine here. 
        rectLabel = new GLabel(theLabel);
        rectLabel.setLocation(
                    x + (width / 2) - (rectLabel.getWidth() / 2),
                    y + (height / 2) + (rectLabel.getAscent() / 2.2));
    }

    public void setLocation(double x, double y)
    {
        //Setting GRect's location. Works correctly./
        super.setLocation(x, y);

        //Attempt to set the label's location 
        //and get a NullPointer exception for rectLabel
        rectLabel.setLocation(
                super.getX() - (rectLabel.getWidth() / 2),
                super.getY() - (rectLabel.getHeight() / 2));
    }
}
Robin Green
  • 32,079
  • 16
  • 104
  • 187
Mimetic
  • 381
  • 2
  • 7

1 Answers1

2

It's hard to reason about exactly what might be setting the variable to null given that it's a public field. That's the first thing you should change. Fields should almost always be private.

Next, you say setLocation is an overridden method - is it called in the GRect constructor by any chance? If so, that will be called before the value is set within the labeledRect constructor, which could well be the cause of your problem.

In Java, the superclass constructor is executed before any of the code in the subclass - both before instance variable initializers and the constructor body. All variables will have their default values. This is why it's a bad idea to call virtual methods from constructors.

(As an aside, the name labeledRect doesn't follow Java naming conventions.)

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • The public field was a temporary workaround that I forgot to change. As for the the class name beginning with a lowercase character, I blame the early morning :P Also, noobness. You were correct about "setLocation()" being called in GRects constructor, so when "super(x,y,width,height)" was called in LabeledRect's constructor, it invoked the overridden version of "setLocation()" while "rectLabel" was null. I thought by using "Super()" that I'd end up calling the non-overridden version of "setLocation()" that didn't contain a reference to "rectLabel". – Mimetic Aug 19 '12 at 22:13
  • My solution was to check if "rectLabel" is null in the overridden "setLocation()" before attempting to call its "setLocation()". However, I'm unsure if this is a suitable fix or if this would be seen as 'hackish' and bad form. (Thanks for the help, by the way) – Mimetic Aug 19 '12 at 22:16
  • @Mimetic: It's not really a good solution, no. Are you in control of `GRect`? If you can avoid calling a non-virtual method in the constructor, that would help. – Jon Skeet Aug 20 '12 at 07:37
  • I am not in control of `GRect`. My final solution was, instead of extending `GRect` and adding a `GLabel` to the class, I extended their superclass `GObject` and implemented an interface `GContainer` which allows a `GObject` to contain other `GObjects`. Now, a call to `LabeledRect.setLocation()` sets the location of the container and all `GObjects` within will move accordingly as well without overriding `setLocation()` – Mimetic Aug 20 '12 at 15:39