0

I am trying to validate the format of a date before assigning it to an instance variable in a constructor. Each time, it throws a null pointer exception even though I provide a value for the field each time I try to instantiate an object. I think the problem must be with the parse statement, but I cannot understand why there would be a null value there.

Moreover, I was wondering if it is proper practice to validate a date in the constructor in the first place. Should I have a method doing this instead?

Thank you so much for your help. This is my first exposure to java.

import java.text.*;

public class Photograph {
    private String caption;
    private final String filename;
    private String dateTaken;
    private int rating; //declares fields 

    public Photograph(String caption, String filename) {
        this.caption = caption;
        this.filename = filename;
        this.dateTaken = "1901-01-01";
        this.rating = 0;    //constructor 
    }

    public Photograph(String caption, String filename, String dateTaken,  
int rating) {
        this.caption = caption;
        this.filename = filename;

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        sdf.setLenient(false);

        try {
            sdf.parse(this.dateTaken);
            this.dateTaken = dateTaken;
        } catch (ParseException e) {
            this.dateTaken = "1901-01-01";
        }
    }

}

public static void main(String[] args) {
    // Testing


    //valid date
    Photograph test_photo1 = new Photograph("cap1", "pic1", "2017-09-30", 3);
    System.out.println(test_photo1.toString());
    Photograph test_photo2 = new Photograph("cap2", "pic2", "2017-12-25", 0);
    System.out.println(test_photo2.toString());
    Photograph test_photo3 = new Photograph("cap3", "pic3", "2018-14-25", 5);
    System.out.println(test_photo3.toString());
    Photograph test_photo4 = new Photograph("cap4", "pic4", "2018-03-27", 4);
    System.out.println(test_photo4.toString());
    Photograph test_photo5 = new Photograph("cap5", "pic5", "2018-03-29", 2);
    System.out.println(test_photo5.toString());
    Photograph test_photo6 = new Photograph("cap6", "pic6", "2018-10-21", 9);
    System.out.println(test_photo6.toString());
}
user207421
  • 305,947
  • 44
  • 307
  • 483
werhgsd
  • 1
  • 4
  • This is the resulting error message: Exception in thread "main" java.lang.NullPointerException at java.base/java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1470) at java.base/java.text.DateFormat.parse(DateFormat.java:393) at Photograph.(Photograph.java:25) at Photograph.main(Photograph.java:100) – werhgsd Sep 30 '19 at 20:49
  • 1
    Because you're parsing a null. The field isn't initialized until the next line. Fix the order. – user207421 Sep 30 '19 at 21:25
  • And you should let the parse exception be thrown. The object isn't much use to anybody with that default date. – user207421 Sep 30 '19 at 21:48
  • I recommend you don’t use `SimpleDateFormat`. That class is notoriously troublesome and long outdated. Instead for validation just use `LocalDate.parse(dateTaken)` and catch `DateTimeParseException`. `LocalDate` and `DateTimeParseException` are from [java.time, the modern Java date and time API](https://docs.oracle.com/javase/tutorial/datetime/). – Ole V.V. Oct 01 '19 at 05:42

1 Answers1

1

For your error, it is "quite" simple:

public Photograph(String caption, String filename, String dateTaken,  
int rating) {
  ..
        try {
            sdf.parse(this.dateTaken);
...
        } catch (ParseException e) {
   ...
        }
    }

The this.dateTaken expression resolve to null because you wanted to use dateTaken (the parameter, not the field).

On a side note, I would not use a String to represent a date, but rather a LocalDate: the parse method does exactly what you are trying to do and LocalDate replace java.util.Date and is immutable.

  • This example below use such LocalDate, providing three constructors based on your code. Field are made final and only one constructor (the last) matters.
  • You could probably avoid storing an empty date (the NO_DATE_TAKEN) either by using null logic in the class, or rather Optional. Using one or the other is another question in itself.
    public class Photograph {
        private static final LocalDate NO_DATE_TAKEN = LocalDate.of(1901, Month.JANUARY, 01);

        private final String caption;
        private final final String filename;
        private final LocalDate dateTaken;
        private final int rating; //declares fields 

        public Photograph(String caption, String filename) {
          this(caption, filename, NO_DATE_TAKEN, 0);
        }

        public Photograph(String caption, String filename, String dateTaken,  
    int rating) {
          this(caption, filename, dateTaken == null ? NO_DATE_TAKEN:LocalDate.parse(dateTaken), rating);
        }

        public Photograph(String caption, String filename, LocalDate dateTaken,  
    int rating) {
          this.caption = caption;
          this.filename = filename;
          this.dateTaken = dateTaken == null ? NO_DATE_TAKEN:dateTaken;
          this.rating = rating;
        }

    }

I would personally not use a String to store Date, and I would not use java.util.Date and SimpleDateFormat to parse Date now that we have LocalDate and LocalDate.parse doing the exact thing you are trying to do.

NoDataFound
  • 11,381
  • 33
  • 59