How are these methods being used without being called? - java

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.

Related

How to create immutable object from mutable in java? [duplicate]

This question already has answers here:
How to create immutable objects in Java?
(14 answers)
Closed 2 years ago.
How to create immutable Planet so the name doesn't change? I am struggling as I think it is immutable project with mutable object. Correct me if I am wrong.
Every time I change name in the output also changes. Am I missing something?
I tried to do all fields private and final (not in this example) but I think I am missing some code to work.
I know java.util.Date is deprecated but this is just for example.
import java.util.Date;
public final class Planet {
String name;
private final Date discoveryDate;
public Planet (String name, Date discoveryDate) {
this.name = name;
this.discoveryDate = new Date(discoveryDate.getTime());
}
public String getName()
return name;
}
public Date getDiscoveryDate() {
return new Date(discoveryDate.getTime());
}
public static void main(String [] args) {
Planet Earth = new Planet("Earth Planet", new Date(2020,01,16,17,28));
System.out.println("Earth");
System.out.println("------------------------------------");
System.out.println("Earth.getName: " + Earth.getName());
System.out.println("Earth.getDiscoveryDate: " + Earth.getDiscoveryDate());
}
}
tl;dr
Either:
Make a record like this, in Java 16 and later:public record Planet( String name , LocalDate discovered ) {}
Or, before Java 16, make a class where you:
Mark all member fields final and private.
Make getter methods as needed, but no setter methods.
Record
Just use the new records feature in Java 16 (previewed in Java 15).
Define your class as a record when its main job is to transparently and immutably carry data. The compiler implicitly creates a constructor, the getters, hashCode & equals, and toString.
Notice that the getter methods implicitly defined in a record do not begin with the JavaBeans-style get… wording. The getter method is simply the name of member field as defined in the parentheses following the class name.
Of course, if your getter methods provide access to an object that is itself mutable, being contained in a record does nothing to stop the calling programmer from mutating the contained object. Notice in the example class next that both String and LocalDate classes are themselves immutable by design. So the mutability of a contained object is a non-issue here.
package org.example;
import java.time.LocalDate;
public record Planet( String name , LocalDate discovered )
{
}
Using that record.
Planet Earth = new Planet( "Earth" , LocalDate.of( 2020 , 1 , 16 ) );
System.out.println( "Earth" );
System.out.println( "------------------------------------" );
System.out.println( "Earth.name: " + Earth.name() );
System.out.println( "Earth.discovered: " + Earth.discovered() );
When run.
Earth
------------------------------------
Earth.name: Earth
Earth.discovered: 2020-01-16
Class
Without the records feature, to make sure a class is immutable you should:
Mark the member fields final. This means the field cannot be assigned a different object after the constructor has finished.
Mark the member fields private. This means objects of other classes will not have direct access to read or change those fields.
Provide getter methods, if needed, but no setter methods. By convention, the JavaBeans-style get… or is… naming is used.
You should also provide appropriate override implementations of hashCode, equals, and toString. Your IDE will help generate the source code for those.
package org.example;
import java.time.LocalDate;
import java.util.Objects;
public class Planète
{
// Member fields
final String name;
final LocalDate discovered;
// Constructors
public Planète ( String name , LocalDate discovered )
{
Objects.requireNonNull( name );
Objects.requireNonNull( discovered );
this.name = name;
this.discovered = discovered;
}
// Getters (read-only immutable class, no setters)
public String getName ( ) { return this.name; }
public LocalDate getDiscovered ( ) { return this.discovered; }
// Object class overrides
#Override
public boolean equals ( Object o )
{
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
Planète planète = ( Planète ) o;
return getName().equals( planète.getName() ) && getDiscovered().equals( planète.getDiscovered() );
}
#Override
public int hashCode ( )
{
return Objects.hash( getName() , getDiscovered() );
}
#Override
public String toString ( )
{
return "Planète{ " +
"name='" + name + '\'' +
" | discovered=" + discovered +
" }";
}
}
Using that class.
Planète Earth = new Planète( "Earth" , LocalDate.of( 2020 , 1 , 16 ) );
System.out.println( "Earth" );
System.out.println( "------------------------------------" );
System.out.println( "Earth.getName: " + Earth.getName() );
System.out.println( "Earth.getDiscoveryDate: " + Earth.getDiscovered() );
Side issues
Do not start a decimal integer literal with 0. The leading zero makes the number octal rather decimal. So your code passing 2020,01,16 should be 2020,1,16.
Never use the Date class, nor Calendar or SimpleDateFormat. These terrible classes are now legacy, supplanted years ago by the modern java.time classes defined in JSR 310. In code above, we used java.time.LocalDate to represent a date-only value, without a time-of-day and without a time zone.
Planet is immutable but field name should be private.

How do I solve this NullPointerException Problem?

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.

Making a getter that accesses another class in JAVA

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.

Why reference object has 04/10/2016? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
I didn't understand why date2 printing 04/10/2016,
1) i didn't call method which has RETURN
2) there are two methods, with same Parameter but diff output
MyDate date2 = new MyDate(4,10,2008);
System.out.println(date2);
----------------------------------------
//MyDate class
public class MyDate{
public int day;
public int month;
public int year;
public MyDate(){
}
//Constructor that takes 3 arguments
public MyDate(int m, int d, int y){
setDate(m, d, y);
}
//Methods
public String toString1(){
return day + "/" + month + "/" + year;
}
public String toString(){
return month + "/" + day + "/" + year;
}
public void setDate(int m, int d, int y){
day = d;
year = y;
month = m;
}
}
It internally invokes toString() when you try to convert an Object to String and in your case which is implemented to print it
I didn't understand why date2 printing 04/10/2016,
With the MyDate class and its toString() method, System.out.println(date2) should print 4/10/2016 since an int is not a String formater and so doesn't add a 0 prefix.
public class MyDate{
public int day;
public int month;
public int year;
...
public String toString(){
return month + "/" + day + "/" + year;
}
...
}
Edit for comment
As you are beginner, I will try to decompose but stay simple.
Your interrogation comes from what this method does :
System.out.println(date2);
First : you have to understand what this line means.
Here, you call the println() method of the out PrintStream field of the System class and you provide as parameter of the method the date2 variable which is an instance of MyDate.
Which should interest you here is what println() does.
The naming may make you guess that it has a relation with the display of something and it is the case but nothing is better than the javadoc and the source code of a class to understand it.
println() is a overloaded method of PrintStream. So you have to identify which one method is called. The single method which takes as parameter a type compatible with MyDate is void java.io.PrintStream.println(Object x).
For information, all classes derive from the Object class, therefore MyDate IS a Object.
Look at its javadoc :
java.io.PrintStream
void java.io.PrintStream.println(Object x)
Prints an Object and then terminate the line. This method calls at
first String.valueOf(x) to get the printed object's string value, then
behaves as though it invokes print(String) and then println().
Parameters: x The Object to be printed.
It is rather clear what the method does :
it calls String.valueOf(x) to get the String value of the x object
then it print the retrieved String value from x.
But what does String.valueOf(x) ?
There again, javadoc and code source will help you :
/**
* Returns the string representation of the {#code Object} argument.
*
* #param obj an {#code Object}.
* #return if the argument is {#code null}, then a string equal to
* {#code "null"}; otherwise, the value of
* {#code obj.toString()} is returned.
* #see java.lang.Object#toString()
*/
public static String valueOf(Object obj) {
return (obj == null) ? "null" : obj.toString();
}
it returns the result of the toString() method on the obj parameter if obj is not null, otherwhise it returns the "null" String.
Come back to your code now.
Inside, you declare these two methods :
public String toString1(){
return day + "/" + month + "/" + year;
}
public String toString(){
return month + "/" + day + "/" + year;
}
toString() is therefore logically called and you should remove or rename the toString1() method as it is error-prone named.
If you want to format your MyDate instance according to several formats, you should have two methods to perform it but you should avoid mixing a custom method with a toString() method.
You could have for example :
public String toStringInDDMMYYYY(){
...
}
public String toStringInMMDDYYYY(){
...
}
Besides, you could use SimpleDateFormat to format your date and use Date objects rather than custom Date objects but as you start in Java, I may understand what you test different things.

the good way of returning a mutable object

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;
}

Categories