HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Computing age in days with a graphical tool in Java

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
agewithjavagraphicaldaystoolcomputing

Problem

I was in the mood for doing some GUI coding, and eventually I came with this simple program: you enter a date, press the button, and the program tells you temporal difference in days between the input date and current date.

```
package net.coderodde.gui.date;

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import java.util.Date;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class DaysCounter implements ChangeListener, ActionListener {

/**
* The actual frame of this program.
*/
private final JFrame frame;

/**
* The label displaying the currently selected day of month.
*/
private final JLabel dayLabel;

/**
* The slider for choosing the day of month.
*/
private final JSlider daySlider;

/**
* The combo box for choosing the month.
*/
private final JComboBox monthComboBox;

/**
* The text field for inputting the year.
*/
private final JTextField yearTextField;

/**
* The button for initiating the age calculation.
*/
private final JButton computeButton;

/**
* Constructs the graphical user interface.
*/
public DaysCounter() {
// Construct components:
this.frame = new JFrame("Age in days");
this.dayLabel = new JLabel("16");
this.daySlider = new JSlider(1, 31, 16);

String[] months = new String[] {
"January",
"February",
"March",
"April",
"May",
"June",
"Jule",
"August",
"September",
"October",
"November",

Solution

Comments and naming

I'd same some of your comments identifying the fields are rather inane - I can guess that private final JFrame frame; is in fact "the actual frame of this program." By contrast, some of the other comments are pretty useful, but more because the name of the field isn't as descriptive as possible - its almost always better to have your code be self explanatory (through clear formatting and logic, as well as good names) than to comment and explain it. For example dayLabel could probably be more clearly named as currentSelectedDayLabel and then remove the comment.

Likewise, anyone who is even a little familiar with Java (and even people who aren't) can guess/know that the lines

chooseDaySlider.addChangeListener(this);
computeAgeButton.addActionListener(this);


are adding listeners without a comment explaining that.

Lambda function

In your main function you use a lambda, which is fine, but you could do it even more cleanly with a method reference.

public static void main(final String... args) {
    javax.swing.SwingUtilities.invokeLater(DaysCounter::new);
}


Casting

You unnecessarily cast your JSlider to a... JSlider. Just call getValue() directly.

@Override
public void stateChanged(ChangeEvent e) {
    currentSelectedDayLabel.setText("" + chooseDaySlider.getValue());
}


actionPerformed

This part seems pretty good, however I'd say you could refactor the distinct sections into their own methods. Furthermore, the entire "days alive" calculation can be simplified. It's also a little magic number-y, but I'd say these are okay because they make sense - 1000 ms, 60 s, 60 m, 24 hr. If you wanted to make these more explicit you could, but in this case I think they would just make it harder to understand.

/**
 * Initiates the age calculation.
 *
 * @param e the button press event, ignored.
 */
@Override
public void actionPerformed(ActionEvent e) {
    Calendar start = Calendar.getInstance();
    Calendar end = Calendar.getInstance();

    int year = getYear();

    if (year == -1) return;

    start.set(year,
            monthComboBox.getSelectedIndex(),
            chooseDaySlider.getValue());

    if (!isValidDate(start)) {
        JOptionPane.showMessageDialog(null,
                "The input date is invalid!",
                "Error",
                JOptionPane.ERROR_MESSAGE);
        return;
    }

    long days = calculateNumDaysAlive(start.getTime(), end.getTime());

    JOptionPane.showMessageDialog(
            frame,
            "You are " + days + " day" + (days == 1 ? "" : "s") + " old.",
            "",
            JOptionPane.INFORMATION_MESSAGE);
}

/**
 * Gets the numeric year
 *
 * @return the year if it can be determined, otherwise -1.
 */
private int getYear() {
    try {
        return Integer.parseInt(yearTextField.getText());
    } catch (NumberFormatException ex) {
        JOptionPane.showMessageDialog(
                frame,
                "Bad year: " + yearTextField.getText(),
                "Error",
                JOptionPane.ERROR_MESSAGE);
        return -1;
    }
}


Tests

It wouldn't hurt to have some tests either, although given the relative simplicity of the calculations here it probably isn't that big of a deal.

Everything

Here is how I adapted your code in total.

```
package net.coderodde.gui.date;

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import java.util.Date;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class DaysCounter implements ChangeListener, ActionListener {

private final JFrame frame;
private final JLabel currentSelectedDayLabel;
private final JSlider chooseDaySlider;
private final JComboBox monthComboBox;
private final JTextField yearTextField;
private final JButton computeAgeButton;

public DaysCounter() {
this.frame = new JFrame("Age in days");
this.currentSelectedDayLabel = new JLabel("16");
this.chooseDaySlider = new JSlider(1, 31, 16);

String[] months = new String[] {
"January",
"February",
"March",
"April",
"May",
"June",
"Jule",
"August",
"September",
"October",
"November",
"December",
};

this.monthComboBox = new JComboBox(months);
this.yearTextField = new JTextField("1980");
this.computeAgeButton = new JButton("Compute");

chooseDaySlider.addChangeListener(this);
computeAgeButton.addActionListener(this);

Code Snippets

chooseDaySlider.addChangeListener(this);
computeAgeButton.addActionListener(this);
public static void main(final String... args) {
    javax.swing.SwingUtilities.invokeLater(DaysCounter::new);
}
@Override
public void stateChanged(ChangeEvent e) {
    currentSelectedDayLabel.setText("" + chooseDaySlider.getValue());
}
/**
 * Initiates the age calculation.
 *
 * @param e the button press event, ignored.
 */
@Override
public void actionPerformed(ActionEvent e) {
    Calendar start = Calendar.getInstance();
    Calendar end = Calendar.getInstance();

    int year = getYear();

    if (year == -1) return;

    start.set(year,
            monthComboBox.getSelectedIndex(),
            chooseDaySlider.getValue());

    if (!isValidDate(start)) {
        JOptionPane.showMessageDialog(null,
                "The input date is invalid!",
                "Error",
                JOptionPane.ERROR_MESSAGE);
        return;
    }

    long days = calculateNumDaysAlive(start.getTime(), end.getTime());

    JOptionPane.showMessageDialog(
            frame,
            "You are " + days + " day" + (days == 1 ? "" : "s") + " old.",
            "",
            JOptionPane.INFORMATION_MESSAGE);
}

/**
 * Gets the numeric year
 *
 * @return the year if it can be determined, otherwise -1.
 */
private int getYear() {
    try {
        return Integer.parseInt(yearTextField.getText());
    } catch (NumberFormatException ex) {
        JOptionPane.showMessageDialog(
                frame,
                "Bad year: " + yearTextField.getText(),
                "Error",
                JOptionPane.ERROR_MESSAGE);
        return -1;
    }
}
package net.coderodde.gui.date;

import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import java.util.Date;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class DaysCounter implements ChangeListener, ActionListener {

    private final JFrame frame;
    private final JLabel currentSelectedDayLabel;
    private final JSlider chooseDaySlider;
    private final JComboBox monthComboBox;
    private final JTextField yearTextField;
    private final JButton computeAgeButton;

    public DaysCounter() {
        this.frame = new JFrame("Age in days");
        this.currentSelectedDayLabel = new JLabel("16");
        this.chooseDaySlider = new JSlider(1, 31, 16);

        String[] months = new String[] {
                "January",
                "February",
                "March",
                "April",
                "May",
                "June",
                "Jule",
                "August",
                "September",
                "October",
                "November",
                "December",
        };

        this.monthComboBox = new JComboBox(months);
        this.yearTextField = new JTextField("1980");
        this.computeAgeButton = new JButton("Compute");

        chooseDaySlider.addChangeListener(this);
        computeAgeButton.addActionListener(this);

        currentSelectedDayLabel.setHorizontalAlignment(JLabel.CENTER);

        frame.getContentPane().setLayout(new GridLayout(5, 1));
        frame.getContentPane().add(currentSelectedDayLabel);
        frame.getContentPane().add(chooseDaySlider);
        frame.getContentPane().add(monthComboBox);
        frame.getContentPane().add(yearTextField);
        frame.getContentPane().add(computeAgeButton);

        frame.pack();

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setResizable(false);

        // Center out the frame.
        Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();

        frame.setLocation((dim.width - frame.getWidth()) / 2,
                (dim.height - frame.getHeight()) / 2);

        frame.setVisible(true);
    }

    public static void main(final String... args) {
        javax.swing.SwingUtilities.invokeLater(DaysCounter::new);
    }

    /**
     * Change the current day of month.
     *
     * @param e the event object.
     */
    @Override
    public void stateChanged(ChangeEvent e) {
        currentSelectedDayLabel.setText("" + chooseDaySlider.getValue());
    }

    /**
     * Initiates the age calculation.
     *
     * @param e the button press event, ignored.
     */
    @Override
    public void actionPerformed(ActionEvent e) {
        Calen

Context

StackExchange Code Review Q#97794, answer score: 2

Revisions (0)

No revisions yet.