Don’t use floats and doubles in Java to represent Money values (or anything where you need exact values)

Float and double types in Java are approximations – they don’t represent exact values. This is by design to allow faster calculations of approximate values at the cost of exact accuracy.

If you are new to Java, at some point you will run into this, or you’ll come across someone else you work with who may tell you to never use floats and doubles to represent Money, but maybe you’re not sure why. The reason is because of how floats and doubles represent approximate values.

If you’ve never come across this before, try this experiment:

float result = 0.01f + 0.01f + 0.01f;

You would expect this calculation to represent 0.03, but if you compare the value of result with 0.03f you’ll find this snippet of code unexpectedly prints false:

if(result == 0.03f){
    System.out.println("true");
}
else{
    System.out.println("false");
}

To represent accurate floating point values use BigDecimal. Alternatively, money values can be represented as an integer value in cents or pennies (for example) to avoid the approximation issues.

tldr; Don’t use floats and doubles to represent money values in Java.

Java local variable vs class field initialization and default values

This is a common error when starting out with Java:

DefaultValues.java:5: error: variable example might not have been initialized
        System.out.println(example);

In Java, Class instance properties are initialized to default values if not explicitly defined with an initial value. For local variables however (e.g. variables local within method or a block scope), default values are not automatically assigned, meaning that you must explicitly initialize them otherwise you’ll see the compile error above.

This is described in the Java tutorial here.

Maven vs Gradle equivalent commands

I’ve seen Gradle becoming more popular in recent years, but it wasn’t too long ago that Maven was the tool for managing dependencies and building/packaging apps.

From my own experience with both, here’s the commonly used equivalent commands:

MavenGradle
mvn compileNo equivalent as gradle build compiles and packages?
mvn packagegradle build
mvn install (see note below)gradle build
mvn testgradle test

There is no direct equivalent to ‘mvn install’ in gradle, but if you depend on a local Maven repo, there is a Gradle plugin to publish packaged artifacts to a Maven repo – see here.

Initializing new Java projects with Gradle

I’ve used Maven for years for dependency management, but Gradle has better support for initializing a new Java source project compared to Maven. Although Maven has archetypes, I can never remember the syntax so usually just copy and paste a pom.xml file from somewhere else.

With Gralde you can ‘gradle init’ a new project and just follow the prompts:

> gradle init

Welcome to Gradle 8.1.1!

Here are the highlights of this release:
 - Stable configuration cache
 - Experimental Kotlin DSL assignment syntax
 - Building with Java 20

For more details see https://docs.gradle.org/8.1.1/release-notes.html

Starting a Gradle Daemon (subsequent builds will be faster)

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Scala
  6: Swift
Enter selection (default: Java) [1..6] 3

Generate multiple subprojects for application? (default: no) [yes, no] n                          nolease enter 'yes' or 'no': 
Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit Jupiter) [1..4] 1

Project name (default: java21playground): 
Source package (default: java21playground): 
Enter target version of Java (min. 7) (default: 21): 
Generate build using new APIs and behavior (some features may change in the next minor release)? (no


> Task :init
Get more help with your project: https://docs.gradle.org/8.1.1/samples/sample_building_java_applications.html

BUILD SUCCESSFUL in 1m 7s
2 actionable tasks: 2 executed

With Maven you add your dependencies to a <dependencies> block in your pom.xml, like

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.8.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Dependencies with the default scope are considered part of your compile and runtime classpath, <scope>test</scope> indicates on the test classpath only.

With Gradle you do the same with your build.gradle properties file, in the dependencies section:

dependencies {
    testImplementation 'junit:junit:4.13.2'

    implementation 'com.google.guava:guava:31.1-jre'
}

implementation dependencies are for runtime, testimplementation are for test only.