Thursday, 24 May 2018

Hello World with Maven

Getting started with Maven is slightly complicated, so I thought I'd write down some links1 2 on how to get up and running quickly with a simple HelloWorld Java application with Maven archetypes.

That and I wanted to tell how to upgrade the Java version used for the Maven project. There seem to be different ways to do it. Some easy, some hard.

Groupidorg.apache.maven
Artifactidthe name of the jar without version, for example maven, commons-math
Version2.0
mvn archetype:generate -DgroupId=com.mrbear -DartifactId=mrbear -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false

Verify that this is the latest archetype version4.

Upgrading to Java 8

One way is to add the versions to the properties in the pom.xml, which is easiest.

<properties>
    <maven.compiler.target>1.8</maven.compiler.target>
    <maven.compiler.source>1.8</maven.compiler.source>
</properties>

Another way is to change the maven compiler plugin to use the new version, which is harder.

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.6.1</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
            </configuration>
        </plugin>
    </plugins>
</build>

References

[1] Apache Maven Project - Creating a simple java application
https://maven.apache.org/plugins-archives/maven-archetype-plugin-1.0-alpha-7/examples/simple.html
[2] Apache Maven Project - Maven in 5 Minutes
https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html
[3] Apache Maven Project - Setting the -source and -target of the Java Compiler
https://maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-source-and-target.html
[4] Apache - Maven Archetype Quickstart Summary
http://maven.apache.org/archetypes/maven-archetype-quickstart/summary.html

Friday, 18 May 2018

Access control modifiers in Java

Just a small blog post.

Some time ago I came across a basic fact in the core Java programming language, that I have interpreted too narrowly for a very long time.

I always have interpreted the "protected" keyword as making the properties or methods of a class available in its subclasses.

Turns out, according to [1] this is not entirely accurate. That's not all it does.

The following table is lifted from the docs in [1]:

ModifierClassPackageSubclassWorld
publicYYYY
protectedYYYN
no modifier Y Y N N
private Y N N N

If you look at the table above, you'll notice that protected is really only one level more secure than public, which was an eye opener for me.

It turns out "protected", isn't really that protected really.

No modifier

If you omit any kind of modifier, you will automatically get the behaviour that the method/property can be accessed only by other classes in the same package.

There are several sources declaring it "no modifier" or "default modifier" or "default protected modifier", but I prefer the "package private modifier" (or just "package-private" for short). The latter is the official Java convention as described in [1].

To my mind it is the best clear short precise definition of what you can do with it.

The JLS2 strictly only mentions "package access".

Conclusion

Apparently there is no Access Modifier in Java that makes methods or fields only be inherited from subclasses3.

However we can achieve the same goal, by putting the subclasses of a class into the same package, and keep this package devoid of all others.

Sealing JARs

Of course, it means that if I create a package with the same name as a package in a JAR file included in my project, I will gain access to all the package private classes in the same package in the JAR.

This problem can apparently be solved by sealing4 the JAR file.

References

[1] Oracle - The Javatm Tutorials - Controlling Access to Members of a Class
https://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html
[2] Oracle - Java Language Spec - JDK 8
https://docs.oracle.com/javase/specs/jls/se8/jls8.pdf
[3] StackOverflow - Why is there no subclasses only access modifier in Java?
https://softwareengineering.stackexchange.com/questions/238581/why-is-there-no-subclasses-only-access-modifier-in-java
[4] Oracle - The Javatm Tutorials - Sealing Packages within a JAR File
https://docs.oracle.com/javase/tutorial/deployment/jar/sealman.html

Thursday, 10 May 2018

Default method 'toString' overrides a member of 'java.lang.Object'

An interface "inherits" methods from the Object class. I wrote about this here1.

The quotes are appropriate, because Interfaces can only inherit from other Interfaces. An interface, when it does not have a super interface, declares all public non-final methods of the Object as members of the Interface (if not explicitly defined in the interface.

I recently attempted something like this TaxBracket2:

It doesn't work. In fact my IDE started complaining immediately with the following message.

Default method 'toString' overrides a member of 'java.lang.Object'

It is supposed to do that, so says the Java Language Spec3:

It is a compile-time error if a default method is override-equivalent with a non-private method of the class Object, because any class implementing the interface will inherit its own implementation of the method.

My colleague helpfully provided the alternative, which is to make an "elementString()" method in the Interface, which can be called in the toString() method of every Class that implements the Interface. (Further down in the JLS, this approach is almost word for word also given)

StackOverflow4 has a helpful link to the JDK mailinglist5 that explains it a lot more in depth.

One of the remarks that really struck a cord with me, is the fact that toString(), equals() and hashCode() are all basically related to the state of a Class, and do not belong in an Interface.

I have therefore decided to remove the offending default method.

References

[1] Do interfaces inherit from object class?
http://randomthoughtsonjavaprogramming.blogspot.nl/2017/07/do-interfaces-inherit-from-object-class.html
[2] Wikipedia - Tax bracket
https://en.wikipedia.org/wiki/Tax_bracket
[3] JLS 8 - 9.4.1.2. Requirements in Overriding
https://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.4.3
[4] StackOverflow - Java8: Why is it forbidden to define a default method for a method from java.lang.Object?
https://stackoverflow.com/questions/24016962/java8-why-is-it-forbidden-to-define-a-default-method-for-a-method-from-java-lan
[5] Malinglist OpenJDK - Allow default methods to override Object's methods
http://mail.openjdk.java.net/pipermail/lambda-dev/2013-March/008435.html

Friday, 4 May 2018

Creating a method reference on a null reference does not throw NullPointerException

We ran into a problem that the Unit tests ran perfectly on my local machine, but the same Unit tests would break in the continuous delivery pipeline.

The problem occurred when creating a method reference. Like so:

When running the test included above, the test failed with:

java.lang.AssertionError: Expected exception: java.lang.NullPointerException

After some research we found out that the difference between the two is the Eclipse compiler used by the IntelliJ IDE vs. the openjdk installed in the continuous delivery pipeline.

We found out that it is illegal to use a method reference on a null reference. Quoting from [1]:

First, if the method reference expression begins with an ExpressionName or a Primary, this subexpression is evaluated. If the subexpression evaluates to null, a NullPointerException is raised, and the method reference expression completes abruptly.

At first glance, this is a bit weird. After all, we want to have a method reference, in order to call it at a later time, which in fact may never occur. So why not have the NullPointerException when an attempt is made to actually call the method?

IntelliJ

IntelliJ comes equipped automatically with the Eclipse Java Compiler (ECJ), and as such it takes some effort to find out which version is installed along with the IDE.

Jar file ecj-4.6.1.jar was include in directory .local/share/JetBrains/Toolbox/apps/IDEA-U/ch-1/181.4445.78/lib/ecj-4.6.1.jar.

In IntelliJ it is possible to provide the path to the ecj jar to use, see [2].

Setting the path to the newly downloaded jar file ~/Downloads/ecj-4.7.3a.jar solved my problem.

It's been a long time since I encountered a bug in the compiler3, but other people have noticed it too4 and then it gets fixed.

Note

Bear in mind that, if we did not use a method reference, but an ordinary lambda, that this problem would not have occurred (immediately).

This means that, if you replace a Lambda with a method reference you may be introducing a NullPointerException earlier in the code without realising it5.

Bear this in mind.

References

[1] JLS 8 - 15.13.3. Run-Time Evaluation of Method References
https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.13.3
[2] IntelliJ - Specifying compilation settings
https://www.jetbrains.com/help/idea/specifying-compilation-settings.html
[3] Eclipse - Bug Report
https://bugs.eclipse.org/bugs/show_bug.cgi?id=521182
[4] StackOverflow - Creating a method reference on a null reference does not throw an exception
https://stackoverflow.com/questions/37681625/creating-a-method-reference-on-a-null-reference-does-not-throw-an-exception
[5] StackOverflow - java.lang.NullPointerException is thrown using a method reference but not a lambda
https://stackoverflow.com/questions/37413106/java-lang-nullpointerexception-is-thrown-using-a-method-reference-but-not-a-lamb/37413546