0

I am writing a java program, that needs some final variables. The java class must be a singleton object. And I can't directly initialize the final variable. Here is my code:

 public class Car {

    private Price price = null;

    //Constructor
    public Car(Price p) {
       this.price = p;
    }

    //method to get the singleton
    private static Car instance = null;       
    public static Car getInstance(Price p) {
       if(instance == null) {
          instance = new ExcelUtil2(p);
       }
       return instance;
    }

    //declare & initialize final variable
    private final Wheel WHEEL_TYPE = getWheelType();

    //get value of the final variable
    public Wheel getWheelType() {

        Wheel wheel = Car.createWheel();

        if(price.getAmount() > 30000){
            wheel.setWheelType("Alloy");
        }else{
            wheel.setWheelType("Alluminium");
        }

        return wheel;
    }
}

And I would like to know whether if I can do like this or not:

private final Wheel WHEEL_TYPE = getWheelType();

That is my first question.

And the next thing is, when I run it I am getting nullPointerException at:

price.getAmount()

in public Wheel getWheelType() method.

I am initializing price using a public constructor.

I am initilizing the class in some other class like this:

Car car = Car.getInstance(price);

Here I verified that both the price object and price.getAmount() are not null.

Can anyone guide me what am I doing wrong? Thanks

The Guest
  • 698
  • 11
  • 27
  • 7
    Well what happened when you tried it? – resueman Nov 17 '15 at 19:28
  • 1
    You might want to think about when the field initializers are executed vs when the `this.price = p;` statement in your constructor body gets called... – Jon Skeet Nov 17 '15 at 19:32
  • "WHEEL_TYPE = getWheelType();' should work. You can also not initialize 'wheel' at declaration (see https://en.wikipedia.org/wiki/Final_%28Java%29#Blank_final) and do the initialization in the constructor. – TR1 Nov 17 '15 at 19:34
  • You can do: private final Wheel WHEEL_TYPE = getWheelType(); – Aragorn Nov 17 '15 at 19:35
  • @resueman Please see my edit – The Guest Nov 17 '15 at 19:41

3 Answers3

6

There's nothing inherently wrong with

private final Wheel WHEEL_TYPE = getWheelType();

In general, though, (as recommended in the Java tutorial), when you do that you should be calling method that cannot be overridden—a final method, a static method, a private method, or a method in a final class.

However, with your particular code there is a problem. In the execution of getWheelType(), you are calling price.getAmount() and price gets initialized in the body of the constructor. Unfortunately for your design, field instance initializers are executed before the body of the constructor, so you will end up calling price.getAmount() before price is initialized.

I recommend that you simply move the assignment to WHEEL_TYPE to inside the constructor, after the assignment to price.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • If I move the assignment to inside the constructor then I can't make the variable final, right? – The Guest Nov 17 '15 at 19:59
  • 1
    @TheGuest - Sure you can. `final` fields can be assigned (once) in a constructor. If you have several constructors, each must ensure that the field is assigned exactly once, either in the body of the constructor or by calling another constructor. – Ted Hopp Nov 17 '15 at 20:03
  • And I would like to know if this is a bad design or ok to follow. Because my project is purely standalone java project (no spring, no ejb, no struts, no jdbc and etc; only java) – The Guest Nov 17 '15 at 20:15
  • 1
    @TheGuest - It's very common, and perfectly fine design, to move complex initialization logic out of the constructor and into a helper method like that. The only issue is that the compiler will not trace such calls to ensure that `final` methods are definitely assigned exactly once; the assignments _must_ be done where the compiler expects them: in the constructor(s), in instance initializers, or in instance initializer blocks. – Ted Hopp Nov 17 '15 at 20:21
1

You are always allowed to initialize a final variable. The compiler makes sure that you can do it only once.

Excerpt from an answer here: How final keyword works

Community
  • 1
  • 1
Aragorn
  • 5,021
  • 5
  • 26
  • 37
1

As Ed mentioned, your price is an instance variable and it is used inside the instance method getWheelType(), which is OK. But the problem is this method is used to create a final variable. The final variable is assigned before the instance variable p is assigned.

If you really want to have your code structure, you may consider using a Builder pattern. My code is not well considered, but it will be something like this.

public class CarTwo {
   private Price price = null;
   private Builder b;
   public Builder getBuilder(){
      return b;
   }

   private final Wheel WHEEL_TYPE;

   private CarTwo(Builder b) {
      this.b = b;
      this.price = b.getPrice();
      WHEEL_TYPE = b.getWheelType();
   }

   private static CarTwo instance = null;       
   public static CarTwo getInstance(Builder builder) {
      if(instance == null) {
         instance = new ExcelUtil2(builder);
      }
      return instance;
   }

   public static class Builder{
     private Price pr;
     public Builder(Price price){
        pr = price;
     }

     public Price getPrice(){
        return pr;
     }
     public Wheel getWheelType() {
        Wheel wheel = CarTwo.createWheel();

        if(pr.getAmount() > 30000){
           wheel.setWheelType("Alloy");
        }else{
           wheel.setWheelType("Alluminium");
        }

        return wheel;
     }
  }
}

Then, you can create the CarTwo object like this:

CarTwo.Builder b = new CarTwo.Builder(new Price());  
CarTwo ct = CarTwo.getInstance(b);    
Jihwan
  • 99
  • 3