The #KFCStandard pom.xml file : UPDATE

      No Comments on The #KFCStandard pom.xml file : UPDATE

The following article was first published in August 2015. There are some minor updates since then so I am republishing the article here on my new blog domain.

The heart of a Maven managed project is the Project Object Model file named pom.xml. This determines what actions Maven will carry out. What follows is the pom file I require my students to use in their Software Development Project where they code a desktop application. Let’s look at this #KFCStandard pom.xml one section at a time.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" 
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
               http://maven.apache.org/xsd/maven-4.0.0.xsd">

The first line is the XML declaration. The W3C states that the current version of XML is 1.0. The encoding refers to the character set that is used in the document. UTF-8 is the most common form of encoding and it means that the document fully supports Unicode.

The next line contains the XML root tag named project. Following the tag are references to the namespace and schema used in the document. An XML validator uses this information to verify that the tags you use in the document are legal Maven pom tags.

 <modelVersion>4.0.0</modelVersion>

The modelVersion tag defines the version of the object model that the pom is using. It is required and as of Maven version 2.x it has been 4.0.0 and remains so for Maven 3.x.

    <groupId>com.kenfogel</groupId>
    <artifactId>FXMavenDemo</artifactId>
    <version>1.0-SNAPSHOT</version>

These three tags make up the GAV. The groupId, artifactId and version taken together represent the unique name for the project. When you look at the dependency section you will see these tags and Maven uses them to locate the libraries you may need to add to your code. If your code becomes a dependency for another project then this is how you will identify it.

The groupId tag typically defines a package name although it can be any string of characters. Most organizations will use their company’s domain name reversed for the groupId and as the first part of all the packages their developers create. The artifactId is typically the name of the project.

The version tag defines the current version of the project. Whatever you place here will be appended to the JAR file that Maven creates. You are free to use any versioning scheme that you want such as 1.0.0 or Best Version Ever Mark 2. A common convention in versioning is to include the string -SNAPSHOT to indicate a project under development.

    <packaging>jar</packaging>

The packaging tag tells us the format of the file that will be built. The choices are jar, war and ear.

    <name>FXMavenDemo</name>
    or
    <name>${project.artifactId}</name>

The optional name tag allows you to associate a name with the build. This name can contain any characters including spaces. If you do not have a name element Maven will use the artifactId. The second example just shows how the artifactId could be expressed by referring to the tag it was contained in.

 <description>A sample pom.xml file for this article</description>

The optional description tag lets you write any text that want to provide information to someone reading this file.

    <developers>
        <developer>
            <id>Enter your school id</id>
            <name>Enter your name</name>
            <email>Enter your email address</email>
        </developer>
    </developers>

The optional developers tag and its child tags allow you to identify the members of a project. I require my students to use this section to identify themselves.

 <organization>
     <name>Enter school name</name>
 </organization>

The optional organization tag is used to identify the company that you work for. Historically it was required when delivering either applets or applications by means of the web but this is seldom done now for security reasons. If you are a student then the organization is the the school you are attending.

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <mainClass>${project.groupId}.MainApp</mainClass>
    </properties>

The properties section allows you to declare variables that can be used in later sections of the pom. The source encoding defines the character set that is used in the generated files produced by Maven.

Maven runs the Java compiler. The compiler source is the version of Java to use for compiling the code. Over the history of Java the format and organization of the class and jar files have changed. The target indicates what format you want for these files.

If the program is an executable for the desktop you can identify which class has the main method. This consists of the package and the class name. Here I am using the groupId as I use this as the root of all packages in a project. It can be any valid package name and class name in your project.

There are two cases in a pom where a plugin needs to know the full path to the class file that contains the main method. The exec plugin needs to know where to start the program when it runs. The shade plugin must list the main class in the jar’s MANIFEST.MF file.

<dependencies>

The most significant function that Maven provides is the management of the required libraries for projects. Libraries must be in a project`s build path. This build path may outside the jar usually in the local Maven repository. The dependency jars can be included in the main jar and the contents of this jar is the build path.

In the dependencies section of a pom file you place a dependency block that must have the groupId, artifactId and version of a library you plan to use. When Maven process the pom file it will look for the files required for the dependency in the local Maven repository, the .m2 folder. If it’s not found here Maven will go online to the standard repository, maven.org, and use the information there to download the necessary files to .m2. If it cannot be found anywhere then an error will occur. Not every library can be located through maven.org so there is a repository tag that can be used in a pom if you wish to have Maven look in other online repositories. See http://bit.ly/1IlaoVx for more details on using multiple repositories.

        <!-- The dependency for the SLF4J Facade -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <!-- Binding for Log4J -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.8.2</version>
        </dependency>
        <!-- Logging Framework Dependency Uses the log4j2 library -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.8.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.8.2</version>
        </dependency>

This first block of dependencies defines the logging library we will use. There are two parts to this particular logging approach. The first two dependency blocks define the SLF4J or Simple Logging Facade for Java. A façade is used to provide an interface for logging that is independent from the specific implementation of a logging framework. The second two dependency blocks define log4J as the logging implementation.

        <!-- JUnit 4 testing dependency -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

This is the dependency for the JUnit testing framework. It includes an additional tag named scope. A scope of tests means that this dependency is only used during the test phase of the build. The JUnit jar will not be included in the production jar.

From this point on you can add any additional dependencies that you may need.

<dependency>
   <groupId>mysql</groupId>
   <artifactId>mysql-connector-java</artifactId>
   <version>5.1.43</version>
</dependency>

Students in my courses almost always need MySQL.

   <dependency>
      <groupId>org.jodd</groupId>
      <artifactId>jodd-mail</artifactId>
      <version>3.8.6</version>
   </dependency>

Students in my third year project course also need Jodd Mail.

</dependencies>

The dependencies are complete and now we move on to the build.

<build>

The build section contains the information that Maven needs to compile, assemble and execute the project. Plugins that define operations that must be carried out in addition to the standard Maven tasks are listed here.

<defaultGoal>clean compile package exec:exec</defaultGoal>

The default life cycle is made up of goals.The tag defaultGoal is used to define the Maven goals, sometimes also called phases. Goals are actions that Maven must carry out. Most IDEs allow you to declare the goals that you want in their run command. If you do that then it will override what you see here. In this tag I have the most common goals.

Clean deletes all class files and jar files. If you use clean then you must close any running instance of your project. Clean deletes the target folder. Your code is run from the target folder so it cannot be deleted if a process is running somewhere from the folder.

Compile does just that. If you do not use clean first then only source code files newer than their compiled class files will be compiled. For small projects clean is not an issue but in large systems you will not necessarily want to clean for every compile.

Package means to create the JAR file. All the class files produced by the compile goal are added to the jar file. The dependencies are also included in the package.

Exec means to execute the program. There are two flavours of exec, one is exec:java and the other is exec:exec. You must use one of these. The exec:exec runs the packaged jar as if it were being run from the command line and in a separate Java Virtual Machine from the IDE and Maven. This will fail if the dependencies are not in the jar.

The exec:java runs the program from the compiled class files and not the jar within the same JVM as the IDE. The Maven dependencies are in the build path so they are available to the executing code. The exec:exec gives a better picture of how your code will perform on its own. On a slow machine exec:java will execute faster because a new instance of the JVM is not needed.

<plugins>

The plugins are the external components that carry out tasks that are not part of the default behaviour of Maven. These plugins are files that are retrieved from a repository for the use of Maven. They are stored in your local repository alongside the dependencies.

             <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation=
                      "org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>${mainClass}</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

This is the Shade plugin. It is responsible for creating an executable jar file. To be a standalone executable jar there are two changes that have to be made to a basic jar. The first is to include all the dependencies in the jar. The second is to create a text file called MANIFEST.MF in the META_INF folder in the jar that contains the name of the class where the main method is found. The execution of shade is defined in the executions tag to occur as part of the package phase when the plain jar is made. You will end up with two jars when this is done. The original and the executable. The following image gives you a sense of the difference between a plain jar and and an executable or shaded jar.

Image of the directory structure of a jar file.

The first jar in the image is the shaded jar and you ca see that all the dependencies are included in the jar. The second jar just contains the compiled code. This second jar will require that all the dependencies are already loaded into the computer and can be found in the build path. You can double click on a shaded jar to run it but not a regular jar to execute it.

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
                <executions>
                    <execution>
                        <id>default-cli</id>
                        <goals>
                            <goal>exec</goal>
                            <goal>java</goal>
                        </goals>
                        <configuration>
                            <mainClass>${mainClass}</mainClass>
                           <executable>${java.home}/bin/java</executable>
                            <commandlineArgs>-jar ${project.build.directory}/${project.build.finalName}.jar</commandlineArgs>
                        </configuration>

                    </execution>
                </executions>
            </plugin>

The exec plugin defines how the program will be run. As there are two ways to run a program, exec:exec and exec:java they are both defined here.

If you are using exec:java then the mainClass tag is used to identify which class file contains the main method.

If you are using exec:exec then the executable tag defines where the java virtual machine, either java.exe or javaw.exe, can be found. The commandlineArgs defines the command to run the executable jar in a separate JVM as if from the command line.

The values for all the $ values except ${mainClass}, that was defined in properties, are provided by the IDE. If you are not using an IDE then you will need to define these, such as ${java.home}, as an environment variable in the operating system.

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.20</version>
                <configuration>
                    <argLine>-Dfile.encoding=${project.build.sourceEncoding}</argLine>
                    <skipTests>false</skipTests> 
                </configuration>
            </plugin>

The last plugin at the end of the pom file is Surefire. While Maven will run JUnit tests by default, this plugin writes the results of the test to a text file and an xml file. These results can also be seen in the console. The argLine element is required to eliminate a warning when the test are run. The configuration skipTests determines if the tests will execute are not. When set to true will ignore the tests. Use with caution as forgetting to set it back to false may result in delivering code you think works but really does not.

A complete pom follows. It is commented to remind you what each part is responsible for. This is the pom file my students must use in the work they do for me. If you want to learn more about the pom visit http://maven.apache.org/pom.html

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

    <!-- Maven version of the xml document currently only 4.0.0 is valid -->
    <modelVersion>4.0.0</modelVersion>

    <!-- The GAV consists of an arbitrary descriptor that is usually in the
    form of a reverse domain name. -->
    <groupId>com.kfdesktopstandard</groupId>

    <!-- This is the name given to the packaged build -->
    <artifactId>kf_desktop_standard_project</artifactId>

    <!-- The version of the build. Any value is valid though a number and a
    string are common. SNAPSHOT means a project under development. FINAL or no
    text is commonly used to refer to stable production version -->
    <version>1.1-SNAPSHOT</version>

    <!-- Default value is jar but may be war or ear -->
    <packaging>jar</packaging>

    <!-- The name given to the project. Unlike groupId and artifactId a name
    may have spaces. By default it is the following so it is optional -->
    <name>${project.artifactId}</name>

    <!-- A description of the program -->
    <description>Standard starting point for JavaFX programs for students of Ken Fogel
        that displays a table of data using JavaFX and JDBC</description>

    <!-- Identifies the programmer or programmers who worked on the project -->
    <developers>
        <developer>
            <id>Enter your school id</id>
            <name>Enter your name</name>
            <email>Enter your email address</email>
        </developer>
    </developers>

    <!-- The company or organization that the programmer(s) work for -->
    <organization>
        <name>Enter school name</name>
    </organization>

    <!-- Global settings for the project. Settings can be accessed in the pom
    by placing the tag name in ${...} ex. ${mainClass} -->
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>

        <!-- class that has the main method -->
        <mainClass>${project.groupId}.MainApp</mainClass>
    </properties>

    <dependencies>

        <!-- The dependency for the SLF4J Facade -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.25</version>
        </dependency>
        <!-- Binding for Log4J -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-slf4j-impl</artifactId>
            <version>2.8.2</version>
        </dependency>
        <!-- Logging Framework Dependency Uses the log4j2 library -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.8.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.8.2</version>
        </dependency>

        <!-- JUnit 4 testing dependency -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <!-- only to be used during test, phase will not be included in executable jar -->
            <scope>test</scope>
        </dependency>

        <!-- MySQL dependency -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.43</version>
        </dependency>


    </dependencies>

    <build>
        <!-- Goals may be set in the IDE or the pom IDE or CLI goals override the
        defaultGoal -->
        <defaultGoal>clean compile package exec:exec</defaultGoal>

        <!-- Plugins define components that perform actions -->
        <plugins>
            <!-- Shade: Create an executable jar containing all the dependencies when
            the package goal is carried out -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.0.0</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation=
                      "org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>${mainClass}</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <!-- Exec: Executes the program -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>1.6.0</version>
                <executions>
                    <execution>
                        <id>default-cli</id>
                        <goals>
                            <!-- Runs in separate instance of JVM -->
                            <goal>exec</goal>
                            <!-- Runs in same instance of the JVM as Maven -->
                            <goal>java</goal>
                        </goals>
                        <configuration>
                            <!--used by java goal -->
                            <!--executes in the same VM that Maven runs in -->
                            <mainClass>${mainClass}</mainClass>

                            <!--used by exec goal -->
                            <!--runs in a separate VM from the one that Maven runs in -->
                            <executable>${java.home}/bin/java</executable>
                            <commandlineArgs>-jar ${project.build.directory}/${project.build.finalName}.jar</commandlineArgs>
                        </configuration>

                    </execution>
                </executions>
            </plugin>

            <!-- Executes JUnit tests and writes the results as an xml and
            txt file Test classes must include one of the following in their
            name: Test* *Test *TestCase -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.20</version>
                <configuration>
                    <argLine>-Dfile.encoding=${project.build.sourceEncoding}</argLine>
                    <skipTests>false</skipTests>
                </configuration>
            </plugin>

        </plugins>
    </build>
</project>
The Complete pom.xml
Email this to someoneShare on Google+0Share on Facebook1Tweet about this on Twitter0Share on LinkedIn9

The Bean Class for Java Programming

      16 Comments on The Bean Class for Java Programming

In the courses that you take with me I define a bean as a class used to aggregate or collect both primitive data types and other classes for modelling data in a program. Most programs require the definition of multiple elements or values to describe the data that they interact with. Rather than list each element individually, these elements are grouped together into a single class.

The original concept of a bean called a JavaBean dates to 1996. It was intended to be a component model for visual elements of a program. These components could then be easily manipulated by a visual modelling tool. You can read the current specification at http://tinyurl.com/ybzwmldq that was last updated in 1997.

I require you to use a subset of this original concept that I refer to as a bean class in the programs that you write for the courses you are taking with me. Here is the rundown of the rules for a bean in my courses:

Mandatory:

  • All fields, also called instance variables or properties, can only be private.
  • There must be a default constructor.
  • Methods to read or write the instance variable must begin with the prefixes set or get.
  • The bean class should implement the serializable interface

Optional:

  • The bean class should have a toString method.
  • The bean class should have a hashCode and an equals method
  • The bean class should implement the Comparable interface and have a compareTo method.

Bonus:

  • The companion Comparable object for when you need to have more than one possible comparison
  • The Java 8 lambda implementation of of a Comparator
  • The Java 8 functional implementation of a Comparator

For example, consider a problem where it is necessary to model a book. Here is the basic bean.

public class Book implements Serializable{

    private String isbn;
    private String title;
    private String author;
    private String publisher;
    private int pages;

    /**
     * Default constructor
     */
    public Book() {
        this.isbn = "";
        this.title = "";
        this.author = "";
        this.publisher = "";
        this.pages = 0;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(final String isbn) {
        this.isbn = isbn;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(final String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(final String author) {
        this.author = author;
    }

    public String getPublisher() {
        return publisher;
    }

    public void setPublisher(final String publisher) {
        this.publisher = publisher;
    }

    public int getPages() {
        return pages;
    }

    public void setPages(final int pages) {
        this.pages = pages;
    }
}
Basic Bean 1

The methods for reading and writing must adhere to the naming convention. They must begin with set, get or is all in lower case. The is prefix is an alternative to get when the type of value returned is a boolean. They must be followed by a capital letter. They usually correspond to the private fields in the class. You may also add any additional methods that your logic may require.

You now have a class that models a book. Any part of your code now requires only a single reference to an object of type Book to represent all the data points or fields for a book. Reading or writing to any field must now go through a method. The serializable interface allows the state of the object to be preserved by writing it to disk. For distributed applications, such as web services, this allows the object to be transmitted over the internet. Review the documentation on serialization as not every class type can be serialized without additional code. Primitives and Strings can be serialized without additional code.


Special Note 1:

I had a paragraph here that incorrectly stated that a serializable object required a default constructor and set methods but it was pointed out by Michael Simons‏ @rotnroll666 that this is incorrect. I should have reviewed the documentation that I recommended that you review. Serialization does not require a default constructor or set methods.


public class Book implements Serializable{

    private String isbn;
    private String title;
    private String author;
    private String publisher;
    private int pages;

    /**
     * Default constructor
     */
    public Book() {
        this.isbn = "";
        this.title = "";
        this.author = "";
        this.publisher = "";
        this.pages = 0;
    }

    /**
     * Non-default constructor
     *
     * @param isbn
     * @param title
     * @param author
     * @param publisher
     * @param pages
     */
    public Book(final String isbn, final String title, final String author, final String publisher, final int pages) {
        this.isbn = isbn;
        this.title = title;
        this.author = author;
        this.publisher = publisher;
        this.pages = pages;
    }
Basic Bean 2

Notice that all the parameters in the methods are final. A final variable cannot have its value changed. If you pass a reference as an argument to any method that does not declare its parameter as final you can change the address that the argument points to and this can result in an unintended side effect. Bean classes must never change an argument.


An argument is a value or reference passed to a method. A parameter is the place holder in the method signature that will represent the argument’s value in the method.


Primitive argument types such as int or double have no connection to the original value from the caller so final is not required. However, it is a best practice to declare it final as you should not be changing it.

toString

We now have a proper bean class. There are four additional methods that can be added that will make the bean more useful in certain circumstances. The first of these circumstances is when you need to represent the bean as a string. The most common reason is to support debugging by allowing you to examine the value of each field in the bean in a log statement. This is the job of the toString method.

    @Override
    public String toString() {
        return "Book{" + "isbn=" + isbn + ", title=" + title + ", author=" + author + ", publisher=" + publisher + ", pages=" + pages + '}';
    }
toString

The toString method is inherited from the superclass Object that every class in Java extends. The annotation @Override, while optional, makes it clear that you are overriding a superclass method. What you put into the string is up to you. For example, when placing an object into GUI tree, the toString method is the default for what should appear on screen in the tree and so may return the value of a single field.

hashCode and equals

The next two methods are hashCode and equals. These should always go together whenever your code must test if two objects have identical values in their fields. It may appear that equals is all you need but there is a performance issue here. Our Book class has 4 String fields. When comparing two Book objects it will be necessary to compare every character in every string to determine if they are equal. The hashCode method creates an integer value called a hash code that represents all the values in the object. This value can be compared to the corresponding value in the object we are comparing to. If the value of the hash code is not the same then we know with certainty that they do not possess the same values.

Hash codes are not unique. It is possible that two objects with different values may return the same hash code. Therefore, if hash codes are equal we must call upon the equals method to be certain they are the same. Hash codes are used in Java collections data structures. If you intend to use such a structure, such as a HashMap or a HashSet then you must have hashCode and equals in the bean.

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 61 * hash + Objects.hashCode(this.isbn);
        hash = 61 * hash + Objects.hashCode(this.title);
        hash = 61 * hash + Objects.hashCode(this.author);
        hash = 61 * hash + Objects.hashCode(this.publisher);
        hash = 61 * hash + this.pages;
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Book other = (Book) obj;
        if (this.pages != other.pages) {
            return false;
        }
        if (!Objects.equals(this.isbn, other.isbn)) {
            return false;
        }
        if (!Objects.equals(this.title, other.title)) {
            return false;
        }
        if (!Objects.equals(this.author, other.author)) {
            return false;
        }
        if (!Objects.equals(this.publisher, other.publisher)) {
            return false;
        }
        return true;
    }
hashCode and equals

These two methods were generated by the IDE that I use, NetBeans. Most IDEs such as Eclipse and IntelliJ will generate these methods. You may choose to exclude certain fields from these methods but the fields you are using must be the same in hashCode and equals.


Special Note 2:

It has been mentioned in the comments that as presented this hashCode method is problematic when used in  a Hash data structure. The fields in the class are mutable/changeable and this could result in an object placed in the hash structure that can never be found if a field is changed. This is a type of memory leak. The solution is to make all fields used to construct the hash code final/immutable. 



Special Note 3:

This next section has been rewritten from the first version of this article based on comments from Michael Simons‏ @rotnroll666, Originally I proposed multiple Comparable interfaces and multiple compareTo methods in the bean. Michael pointed out that this will result in code bloat as the bean will keep getting longer with each additional compareTo method. He recommended Comparable objects that exist outside the bean.


Comparable Interface and compareTo

You have two choices for how you can order objects when using sort methods or self sorting collections such as a Map. The first choice is to implement a compareTo method in the bean. This method is required when you implement the Comparable interface. This sorted order that results is called the natural ordering and the compareTo method is referred to as the natural ordering method. You must decide which fields will contribute to the sort order criteria. I wish to be able to sort the books by their title.

    /**
     * Natural comparison method for Title
     *
     * @param book
     * @return negative value, 0, or positive value
     */
    @Override
    public int compareTo(Book book) {
        return this.title.compareTo(book.title);
    }
compareTo method

I am taking advantage of the fact that String objects implement the Comparable interface. If my title, this.title, comes before the title in the Book parameter then the difference in the ASCII codes for the first differing characters is returned as a negative value. If the strings are identical then 0 is returned. If my title comes after the title in the Book parameter then the difference in the ASCII code for the first differing characters is returned as a positive value

Comparator

You can have only one compareTo method that overrides the Comparable interface requirement. This leads to the second approach to comparisons. If you need multiple comparisons each with different fields as the criteria then your best solution is to create Comparable objects. This way you can compare any parts of the objects. You just need a Comparable object for each comparison and the original bean does not change.

package com.kenfogel.book;

import java.util.Comparator;

public class BookPageComparator implements Comparator<Book> {

    /**
     * The interface mandated compare method
     *
     * @param book1
     * @param book2
     * @return negative value, 0, or positive value
     */
    @Override
    public int compare(Book book1, Book book2) {
        return book1.getPages() - book2.getPages();
    }
}
Comparator Class

Comparator lambda

The Comparable interface lends itself to the use of lambda expressions that is demonstrated in the test code. This eliminates the need for a Comparable object. However, if the logic to determine the sorting order is more than three lines long you might be better off with a Comparator object instead. Here is what the the two comparison approaches look like when used to sort an array.

Arrays.sort(myBooks); // uses the Comparable compareTo in the bean
Arrays.sort(myBooks, bookPageComparator); // uses the Comparator object
Arrays.sort(myBooks, (s1, s2) -> {
            return s1.getPublisher().compareTo(s2.getPublisher());
        }); // uses a Comparator lambda
Sorting an array

Java 8 Functional Comparator

Java 8 introduced new methods in the Comparator interface that simplifies comparisons with objects. I wish to sort my books based on the ISBN number. As a String has a natural order, I can inform the sorting method what Comparator to use by the following.

        Arrays.sort(myBooks, comparing(Book::getIsbn)); // Comparable function

See the test program just below to see how Comparable and Comparator work.

The Complete Bean

Here is the final version of the Book bean.

mport java.io.Serializable;
import java.util.Objects;

public class Book implements Serializable, Comparable<Book> {

    private String isbn;
    private String title;
    private String author;
    private String publisher;
    private int pages;

    /**
     * Default constructor
     */
    public Book() {
        this.isbn = "";
        this.title = "";
        this.author = "";
        this.publisher = "";
        this.pages = 0;
    }

    /**
     * Non-default constructor
     *
     * @param isbn
     * @param title
     * @param author
     * @param publisher
     * @param pages
     */
    public Book(final String isbn, final String title, final String author, final String publisher, final int pages) {
        this.isbn = isbn;
        this.title = title;
        this.author = author;
        this.publisher = publisher;
        this.pages = pages;
    }

    public String getIsbn() {
        return isbn;
    }

    public void setIsbn(final String isbn) {
        this.isbn = isbn;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(final String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(final String author) {
        this.author = author;
    }

    public String getPublisher() {
        return publisher;
    }

    public void setPublisher(final String publisher) {
        this.publisher = publisher;
    }

    public int getPages() {
        return pages;
    }

    public void setPages(final int pages) {
        this.pages = pages;
    }

    @Override
    public String toString() {
        return "Book{" + "isbn=" + isbn + ", title=" + title + ", author=" + author + ", publisher=" + publisher + ", pages=" + pages + '}';
    }

    @Override
    public int hashCode() {
        int hash = 7;
        hash = 61 * hash + Objects.hashCode(this.isbn);
        hash = 61 * hash + Objects.hashCode(this.title);
        hash = 61 * hash + Objects.hashCode(this.author);
        hash = 61 * hash + Objects.hashCode(this.publisher);
        hash = 61 * hash + this.pages;
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Book other = (Book) obj;
        if (this.pages != other.pages) {
            return false;
        }
        if (!Objects.equals(this.isbn, other.isbn)) {
            return false;
        }
        if (!Objects.equals(this.title, other.title)) {
            return false;
        }
        if (!Objects.equals(this.author, other.author)) {
            return false;
        }
        if (!Objects.equals(this.publisher, other.publisher)) {
            return false;
        }
        return true;
    }

    /**
     * Natural comparison method for Title
     *
     * @param book
     * @return negative value, 0, or positive value
     */
    @Override
    public int compareTo(Book book) {
        return this.title.compareTo(book.title);
    }
}
Complete Bean

The Bean Tester

Here is a test program that demonstrates the two ways that comparisons can be made, The comments describe which approach is used.

// Required for Arrays.sort
import java.util.Arrays;
// Required by the Comparator function
import static java.util.Comparator.comparing;

public class BeanTester {

    /**
     * Here is where I am testing my comparisons
     *
     */
    public void perform() {
        // Lets create four books
        Book b0 = new Book("200", "Xenon", "Hamilton", "Harcourt", 99);
        Book b1 = new Book("500", "Boron", "Bradbury", "Prentice", 108);
        Book b2 = new Book("300", "Radon", "Heinlein", "Thompson", 98);
        Book b3 = new Book("404", "Argon", "Campbell", "Hachette", 102);

        // Using Comparable to compare two books
        System.out.println("Value returned by Comparable");
        System.out.println(b0.getTitle() + " compared to " + b1.getTitle() + ": "
                + b0.compareTo(b1));
        System.out.println();

        // Using Comparator to compare two books
        System.out.println("Value returned by Comparator");
        BookPageComparator bookPageComparator = new BookPageComparator();
        System.out.println(b0.getPages() + " compared to " + b1.getPages() + ": "
                + bookPageComparator.compare(b0, b1));
        System.out.println();

        // Create an array we can sort
        Book[] myBooks = new Book[4];
        myBooks[0] = b0;
        myBooks[1] = b1;
        myBooks[2] = b2;
        myBooks[3] = b3;
        System.out.println("Unsorted");
        displayBooks(myBooks);

        System.out.println("Sorted with Comparable Interface on Title");
        Arrays.sort(myBooks); // uses the Comparable compareTo in the bean
        displayBooks(myBooks);

        System.out.println("Sorted with Comparable Object on Pages");
        Arrays.sort(myBooks, bookPageComparator); // uses the Comparator object
        displayBooks(myBooks);

        System.out.println("Sorted with Comparable lambda expression on Publishers");
        Arrays.sort(myBooks, (s1, s2) -> {
            return s1.getPublisher().compareTo(s2.getPublisher());
        }); // uses the Comparator lambda
        displayBooks(myBooks);

        System.out.println("Sorted with Comparable lambda function on ISBN");
        Arrays.sort(myBooks, comparing(Book::getIsbn)); // Comparable function
        displayBooks(myBooks);
    }

    /**
     * Print the contents of each Book object in the array
     *
     * @param theBooks
     */
    private void displayBooks(Book[] theBooks) {
        for (Book b : theBooks) {
            System.out.print(b.getIsbn() + "\t");
            System.out.print(b.getTitle() + "\t");
            System.out.print(b.getAuthor() + "\t");
            System.out.print(b.getPublisher() + "\t");
            System.out.println(b.getPages());
        }
        System.out.println();
    }

    /**
     * Where it all begins
     *
     * @param args
     */
    public static void main(String[] args) {
        BeanTester bt = new BeanTester();
        bt.perform();
        System.exit(0);
    }
}
Bean Tester

The output of this program will be:

Value returned by Comparable
Xenon compared to Boron: 22

Value returned by Comparator
99 compared to 108: -9

Unsorted
200	Xenon	Hamilton	Harcourt	99
500	Boron	Bradbury	Prentice	108
300	Radon	Heinlein	Thompson	98
404	Argon	Campbell	Hachette	102

Sorted with Comparable Interface on Title
404	Argon	Campbell	Hachette	102
500	Boron	Bradbury	Prentice	108
300	Radon	Heinlein	Thompson	98
200	Xenon	Hamilton	Harcourt	99

Sorted with Comparable Object on Pages
300	Radon	Heinlein	Thompson	98
200	Xenon	Hamilton	Harcourt	99
404	Argon	Campbell	Hachette	102
500	Boron	Bradbury	Prentice	108

Sorted with Comparable lambda expression on Publishers
404	Argon	Campbell	Hachette	102
200	Xenon	Hamilton	Harcourt	99
500	Boron	Bradbury	Prentice	108
300	Radon	Heinlein	Thompson	98

Sorted with Comparable lambda functions based on ISBN
200	Xenon	Hamilton	Harcourt	99
300	Radon	Heinlein	Thompson	98
404	Argon	Campbell	Hachette	102
500	Boron	Bradbury	Prentice	108
Output

Bean classes are required when using several libraries in Java such as Context Dependency Injection (CDI) and Bean Validation. You will also use bean classes to represent data from a database when using Java Database Connectivity (JDBC). Beans that back JavaFX controls are coded differently but are 100% backward compatible with simple bean classes. See my next article on JavaFX beans.

Email this to someoneShare on Google+0Share on Facebook7Tweet about this on Twitter0Share on LinkedIn19