In here, I'm receiving a NullPointerException problem which seems to be related to my Date object in my class, but I can't figure it out.
My teacher said that everything works except that I'm returning the wrong data types for my date class, but I KNOW I'm returning a string, after all, that is what a getDate() method returns
I've tried putting in the code for the getDate() method itself, as in
"getMonth() + "/" + getDay() + "/" + getYear();
//main employee class
public class Employee
{
//2 attribute for employee
private String name;
private Date dateHired;
public boolean employeeType; //to check what type of employee, if
//false, then employee is salaried, otherwise hourly
//setter methods
public void setDateHired(int m, int d, int y)
{
Date dateHired = new Date(m,d,y);
}
public void setName(String s)
{
name = s;
}
public void setHoursWorked(int w)
{
}
//getter methods
protected String getDateHired()
{
return dateHired.getDate();
}
There isn't supposed to be an error, I reviewed this code hundreds of times and everything seems to be fine!
public void setDateHired(int m, int d, int y)
{
//dateHired is a local variable.. you're not modifying the class instance variable.
Date dateHired = new Date(m,d,y);
}
should be:
public void setDateHired(int m, int d, int y)
{
this.dateHired = new Date(m,d,y);
//or:
//dateHired = new Date(m,d,y);
}
Before turning to your actual questions, you have
private Date dateHired;
I recommend you don’t use the Date class. It was always poorly designed and is now long outdated. Despite its name it also doesn’t represent a date, but a moment in time at which there are two or three different dates around the globe. Instead use LocalDate from java.time, the modern Java date and time API. Use the tutorial link at the bottom.
For the NullPointerException Brandon has already explained how that came about from the following line:
Date dateHired = new Date(m,d,y);
Using LocalDate the line should be:
dateHired = LocalDate.of(y, m, d);
In new Date(m,d,y) you have got the arguments in the wrong order, you are treating year and month incorrectly and you are using a constructor that has been deprecated for decades, never ever do that.
My teacher said that everything works except that I'm returning the
wrong data types for my date class, but I KNOW I'm returning a string,
after all, that is what a getDate() method returns
The getDate method has been deprecated just as long, don’t use that one either. It also doesn’t give you the full date, and your teacher is correct, it doesn’t return a string, it returns an int.
I've tried putting in the code for the getDate() method itself, as in
getMonth() + "/" + getDay() + "/" + getYear();
You’d be surprised. However, rather than getting surprised and confused use a formatter for formatting your LocalDate into a string.
protected String getDateHired()
{
DateTimeFormatter dateFormatter
= DateTimeFormatter.ofLocalizedDate(FormatStyle.SHORT);
return dateHired.format(dateFormatter);
}
Example output from this method assuming US locale and a hire date of December 1, 2018:
12/1/18
ofLocalizedDate gives you a formatter for the default locale, so if your locale differs from US, you may be pleasantly surprised that the format of the string matches your expectations better than the quoted example. Normally one would declare the formatter a constant (a static final variable in the class), but I wasn’t sure that you had learned about this yet, so in this case I didn’t.
Long story short: Using the outdated Date class correctly is a lot harder than you would think because of its poor design. Don’t struggle with that one. Use of the modern LocalDate comes a lot more naturally. It numbers years and months the same way as humans do (at least since year 1 AD) and its method names are clearer, just mention a few of the many advantages.
Tutorial link: Oracle tutorial: Date Time explaining how to use java.time.
Related
I have an Object MyTimes and in that object there are fields name ,start_date and configuration.
I have an array of this object, MyTimes [] mytimes
I am trying to sort the array by the start time but am struggling how to go about it.
The start_time field is a string, so this needs converting to a datetime.
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
for(int i=0; i<mytimes.length; i++) {
Date date = formatter.parse(mytimes[i].getStartTime());
}
I'd then put the date into an array list perhaps and then sort by datetime? But then I wouldnt know which start_time corresponds with which mytimes object...
What is the most efficient way of doing this?
Under the right circumstances this is a one-liner:
Arrays.sort(myTimes, Comparator.comparing(MyTimes::getStartDate));
Let’s see it in action:
MyTimes[] myTimes = {
new MyTimes("Polly", "2019-03-06T17:00:00Z"),
new MyTimes("Margaret", "2019-03-08T09:00:00Z"),
new MyTimes("Jane", "2019-03-01T06:00:00Z")
};
Arrays.sort(myTimes, Comparator.comparing(MyTimes::getStartDate));
Arrays.stream(myTimes).forEach(System.out::println);
Output:
Jane 2019-03-01T06:00:00Z
Polly 2019-03-06T17:00:00Z
Margaret 2019-03-08T09:00:00Z
I am assuming that getStartDate returns an Instant or another type the natural order of which agrees with the chronological order you want. For example:
public class MyTimes {
private String name;
private Instant startDate;
// Constructor, getters, toString, etc.
}
If you are receiving your start dates as strings somehow, you may write a convenient constructor that accepts a string for start date. I am already using such a constructor in the above snippet. One possibility is having two constructors:
public MyTimes(String name, Instant startDate) {
this.name = name;
this.startDate = startDate;
}
public MyTimes(String name, String startDate) {
this(name, Instant.parse(startDate));
}
The Instant class is part of java.time, the modern Java date and time API.
I am exploiting the fact that your strings are in the ISO 8601 format for an instant, the format that Instant.parse accepts and parses.
Avoid SimpleDateFormat and Date
I recommend you don’t use SimpleDateFormat and Date. Those classes are poorly designed and long outdated, the former in particular notoriously troublesome. There is also an error in your format pattern string for parsing: Z (pronounced “Zulu”) means UTC, and of you don’t parse it as such, you will get incorrect times (on most JVMs). Instant.parse efficiently avoids any problems here.
Don’t store date-tine as a string
It looks like you are are storing start time in a String field in your object? That would be poor modelling. Use a proper date-time type. Strings are for interfaces. Date-time classes like Instant offer much more functionality, for example define sort order.
You have two main approaches:
Make your class implement Comparable
Use a custom Comparator
Then, you can choose the field to compare from, and transform it.
IE (implementing comparable):
class Example implements Comparable<Example> {
private String stringDate;
public int compareTo(Example e) {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
Date date1 = formatter.parse(this.stringDate);
Date date2 = formatter.parse(e.stringDate);
return date1.getTime() - date2.getTime();
}
}
And then using Arrays.sort would use your custom comparison.
Let your class implement Comparable and implement compareTo using modern formatting and date classes. Note that LocalDateTime also implements Comparable so once the string has been parsed you let LocalDateTime do the comparison
public class MyTimes implements Comparable<MyTimes> {
private final DateTimeFormatter dtf = DateTimeFormatter.ISO_INSTANT;
//other code
public int compareTo(MyTimes o) {
LocalDateTime thisDate = LocalDateTime.from(dtf.parse(this.getStartTime()));
LocalDateTime otherDate = LocalDateTime.from(dtf.parse(o.getStartTime()));
return thisDate.compareTo(otherDate);
}
}
You can also create a separate class as a comparator if this comparison is special and what you not always want to use
public class MyTimesComparator implements Comparator<MyTimes> {
#Override
public int compare(MyTimes arg0, MyTimes arg1) {
DateTimeFormatter dtf = DateTimeFormatter.ISO_INSTANT;
LocalDateTime thisDate = LocalDateTime.from(dtf.parse(this.getStartTime()));
LocalDateTime otherDate = LocalDateTime.from(dtf.parse(o.getStartTime()));
return thisDate.compareTo(otherDate);
}
}
and then use it like
someList.sort(new MyTimesComparator());
or use an inline function (I am using Instant here)
someList.sort( (m1, m2) -> {
DateTimeFormatter dtf = DateTimeFormatter.ISO_INSTANT;
Instant instant1 = Instant.from(dtf.parse(m1.getStartTime));
Instant instant2 = Instant.from(dtf.parse(m2.getStartTime));
return intant1.compareTo(instant2);
});
I noticed now that you have an array and not a list so you need to convert to a list or use Arrays.sort instead.
Im learning OOP in Java and I am wondering how to create a getter that access data from a seperate class? I have a program that creates people with their names and birth dates. So I have 3 classes, PersonProgram(the main), People, and Date. The People class has the constructor for the Full names which include first name, last name, and birth date. The Date class has the constructor and setters for the date which includes 3 integers for the day, month, and year, and then a method that formats them into a string format. So in my main program, I have an option to print out information about whatever Person is created. So I call the getter of People with
Person[selection].getBirthDate;
and the getter in my People class is:
public Date getBirthDate() {
return birthDate;
}
but I want it to retrieve the formatted date from the toString method in my Date class. Can I just call the toString method from inside the getBirthDate getter?
Yes, it would be appropriate to use toString() here. In Date class:
#Override
public String toString(){
return year + "/" + month + "/" + day;
}
And in People class return a String instead of a Date object. This way you have a clean method interface, as you only want the Date to be read as whole String anyway:
public String getBirthDate() {
return birthDate.toString();
}
You can call from your main method :
person[index].getBirthDate().toString();
This will return the string you want since the toString method will be invoked on the Date class instance embeeded in the person instance.
I have this pared down set of class files as an example of OO. They work perfectly but I do not understand how the println call from WorkerTest.java makes it all the way through Worker.java and to Date.java? Both Worker.java and Date.java have toString methods but neither are explicitly called, but I can tell from the output that both are used.
How is this working or what concept should I be studying?
public class WorkerTest {
public static void main( String[] args ) {
Date birth = new Date( 7, 15, 1922 );
Worker worker = new Worker( birth );
System.out.println( worker );
}
}
public class Worker {
private Date birthday;
public Worker( Date inboundBirthday ) {
birthday = inboundBirthday;
}
public String toString() {
return String.format( "Birthday: %s", birthday );
}
}
public class Date {
private int month;
private int day;
private int year;
public Date( int inboundMonth, int inboundDay, int inboundYear ) {
month = inboundMonth;
day = inboundDay;
year = inboundYear;
}
public String toString() {
return String.format( "%d/%d/%d", month, day, year );
}
}
Output:
Birthday: 7/15/1922
PrintStream.println(obj) calls obj.toString(). (Or more precisely: it calls String.valueOf(obj), which in turn calls obj.toString() unless obj is a null reference.)
See the Javadoc for java.io.PrintStream.
(Maybe what you're missing is that toString is actually a method on java.lang.Object? Worker and Date are merely overriding it. So all objects have that method, and JDK methods can rely on its existence. See the Javadoc for `java.lang.Object for a list of all the methods that all objects have.)
When println encounters a variable it tries to determine how it should be printed. It checks to see if the toString() method for the class in question has been overridden. So here's what's happening: println needs to print an instance of class worker, so it checks for the toString() method inside the Worker class. Inside the worker class it finds this line:
return String.format( "Birthday: %s", birthday );
Now it must figure out how to print birthday. Since birthday is an instance of Date, it checks for Date's toString() method. The key thing in understanding all this is that Java's built in classes have toString() methods too, you just don't see them. This is a good example because it shows you what's happening behind the scenes.
PrintStream.println() explicitly makes a call to String.valueOf(), which in turn calls Object.toString(), which is overridden in your two objects.
I have Date in this format 2009-09-17T00:00:00.000-35:00 . As per the business Rules for my Application , i have written 3 Methods which will accept this Date and returns the Date in MM/yyyy , yyyyMM and dd .
For example one method is shown below MM/yyyy
private String getMonthYear(String date) throws Exception {
DateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS", Locale.US);
String s1 = date;
String s2 = null;
Date d;
try {
d = sdf.parse(s1);
s2 = (new SimpleDateFormat("MM/yyyy")).format(d);
} catch (ParseException e) {
e.printStackTrace();
}
return s2;
}
Similarly i have other two methods which will return data in yyyyMM and dd formats ??
This works fine , but does not look good
My question is can we have only one utility which satisfies my requirement ??
My question is can we have only one utility which satisfies my requirement ??
I think you're going about this the wrong way to start with. Fundamentally the data is just a date. You can apply formats later, when you need to. I suggest you start using Joda Time and make your method return a LocalDate. That captures all the real information, and you can then have three separate DateTimeFormatter objects used to format the value whenever you want.
Wherever you can, represent data using a type which most naturally represents the real information. Get your data into that natural format as early as possible, and keep it in that format until you have to convert it into something else (such as a string).
You could define a single method and receive as a parameter the string with the expected date format, the three strings with the formats could be defined as constants.
Yes, you could group the three methods together, and use an additional argument (an enum, for example) to specify which kind of output format you want. But I would not do that. Your solution is cleaner. Why do you think it doesn't look good?
What I would do, however, is transforming the String to a Date once and for all, and using a Date everywhere rather than the String, and transforming the Date with one of your 3 methods (which would take a Date as argument rather than a String) when needed.
The Apache Commons Lang library already has utility methods to do this for you.
For example:
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang.time.DateUtils;
///
// first, convert the string to a date object
Date parsedDate = DateUtils.parseDate("2012-05-25T16:10:30.000",
new String[] {"yyyy-MM-dd'T'HH:mm:ss.SSS"});
// now, format the date object to a string, using different formats
String formattedDate = DateFormatUtils.format(parsedDate, "MM/yyyy");
String formattedDate2 = DateFormatUtils.format(parsedDate, "yyyyMM");
Take a look at DateFormatUtils and DateUtils for more information.
You could just have a Date class which has the three methods. Like below:
public class MyDate {
private String date = null;
public MyDate(String date) {
this.date = date;
}
public String getMonthYear() {
return null;
}
public String getYearMonth() {
return null;
}
public String getDay() {
return null;
}
}
You can format the String into three different Strings in the constructor and just return those strings on method calls. That implementation would be good if you make numberous/repeated calls on the same date string. Or you could format the string in the method call, if you are doing it once but if you are doing it once you may want to make the class/methods static and get rid of the constructor.
Let's say I have a class Comment and I have a private field named commentDate which is a java.util.Date and with a getter named getCommentDate.
Why it's better to return a copy of that date ( return new Date(commentDate.getTime()) ) than simply returning that date...
How can a user change the object state of that Date since it's a getter, not a setter?
Since java.util.Date implements Cloneable you can easily clone the date, as:
public class DateTest {
private Date date;
public DateTest() {
}
public Date getDate() {
return (Date) date.clone();
}
public void setDate(Date date) {
this.date = (Date) date.clone();
}
}
First off, please, please, please avoid using getters and setters as much as possible. If you have both of them for the same field you are almost certainly doing something wrong. I don't care what the Java gurus are telling you. They don't know what they're talking about. This is not how OO works. OO is not a make-work project to turn field accesses into method calls. That doesn't actually encapsulate anything.
That said: if you return the date itself, then the calling code has a reference to your date object, and can use its full interface. Since dates are mutable objects, the interface includes things that can change the object state. Since the reference is to your date, your date's state will get changed. It doesn't matter how the calling code got the date (i.e. "with a getter").
How can a user change the object state
of that Date since it's a getter, not
a setter?
Easily:
Comment comment = new Comment();
comment.getCommentDate().setTime(0); // now it's January 1, 1970 00:00:00 GMT.
Follow Tapas Bose example, we can do the following using JAVA 8 to handle NULL cases:
public class DateTest {
private Date date;
public DateTest() {
}
public Date getDate() {
return Optional.ofNullable(date).map(Date::getTime).map(Date::new).orElse(null);
}
public void setDate(Date inputDate) {
this.date= Optional.ofNullable(inputDate).map(Date::getTime).map(Date::new).orElse(null);
}}
Reference: Is there a way to copy Date object into another date Object without using a reference? (Nicolas Henneaux's answer)
The user can't "replace" the instance provided by getCommentDate(). However, the user can invoke getCommentDate().setMonth(10) and thereby modifying the date. Thus, if this is a concern, I'd advise you to return a copy of the "original" instance.
Since java.util.Date is mutable, it could be changed via the getter like this:
getCommentDate().setYear(2011)
This will cause the commentDate on the comment to be changed to the year 2011. All other set methods on Date can be called as well off course, just an example.
In Java you are dealing with references. When you've a getter and returning your commentDate then you're in fact returning a reference to the object. That means that it is the same object like in your private field the caller can operate on due to reference returned by getter.
Note: Do not return mutable objects via getters eg. date (before Java 8). It can always be reset by a rogue programmer. Lets say you write a program where social security benefits of an employee is calculated based on the years of work.
public class Employee {
// instance fields
private String name;
private String nickName;
private double salary;
private Date hireDay;
// constructor
Employee(String name, String aNickName, double aSalary, int aYear,
int aMonth, int aDay) {
this.name = name;
nickName = aNickName;
salary = aSalary;
GregorianCalendar cal = new GregorianCalendar(aYear, aMonth - 1, aDay);
hireDay = cal.getTime();
}
//needs to be corrected or improved because date is a mutable object
public Date getHireDay() {
return hireDay;
}
A hacker/bad programmer can reset the date using a setter
Employee john = new Employee("John", "Grant", 50000, 1989, 10, 1);
Date d = john.getHireDay();
// Original hire date is Oct 1, 1989
System.out.println("Original hire date "+ d.getTime()));
long tenYearsInMilliseconds = 10 * 365 * 24 * 60 * 60 * 1000L;
long time = d.getTime();
// Hire date after hacker modifies the code
d.setTime(time - tenYearsInMilliseconds);
System.out.println("Hacked hire date "+john.getHireDay().getTime()));
}
Instead..return a clone of the date method for Java 7 or use LocalDate Class for Java 8
// for Java 7
public Date getHireDay() {
return (Date)hireDay.clone();
}
//for Java 8
public LocalDate getHireDay() {
return hireDay;
}