Java

Incompatible Java 8 change in the Collections classes

Suppose that we want to implement a HashMap (let’s call it OrderedHashMap where the values can also retrieved by their index, where the index refers to the order in which the key/value pairs have been inserted. The following unit test shows how such a class could be used:

@Test
public void testPutGet() {
    OrderedHashMap<string, string=""> ohm = new OrderedHashMap<>();
    ohm.put("Z", "LetterZ");    // 0
    ohm.put("A", "LetterA");    // 1
    ohm.put("D", "LetterD");    // 2
    ohm.put("K", "LetterK");    // 3

    assertEquals(ohm.get(0), "LetterZ");
    assertEquals(ohm.get(3), "LetterK");
}
</string,>

The straight forward implementation of the class OrderedHashMapis to inherit from one of the existing Collection classes and simply implement the additional functionality, e.g. like

package com.example;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;

public class OrderedHashMap<k, v=""> extends LinkedHashMap<k, v=""> {
    private static final long serialVersionUID = 6354582314971513369L;

    private List<v> items = new ArrayList<>();

    public V get(int index) {
        return items.get(index);
    }

    @Override
    public V put(K key, V value) {
        items.add(value);
        return super.put(key, value);
    }
}
</v></k,></k,>

With this code, the above unit test succeeds. Lets add an additional unit tests for one of the other methods inherited from LinkedHashMap, putAll():

@Test
public void testPutAll() {
    OrderedHashMap<string, string=""> ohm = new OrderedHashMap<>();
    ohm.put("Z", "LetterZ");    // 0
    ohm.put("A", "LetterA");    // 1
    ohm.put("D", "LetterD");    // 2
    ohm.put("K", "LetterK");    // 3

    OrderedHashMap<string, string=""> ohm2 = new OrderedHashMap<>();
    ohm2.putAll(ohm);

    assertEquals(ohm2.get(0), "LetterZ");
    assertEquals(ohm2.get(3), "LetterK");
}
</string,></string,>

This unit test also succeeds and shows that we can copy the elements from one OrderedHashMap to a second one, using putAll(). Unless we switch to Java 8. If we run the above sample with the Java 8 runtime, the test fails with an exception:

java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
	at java.util.ArrayList.rangeCheck(Unknown Source)
	at java.util.ArrayList.get(Unknown Source)
	at com.example.OrderedHashMap.get(OrderedHashMap.java:13)
	at com.example.test.OrderedHashMapTest.testPutAll(OrderedHashMapTest.java:37)
	...

This is an issue I came across when I was trying to translate a grammar with ANTLR (not using the latest version) – see also https://github.com/antlr/antlr4/issues/337. The background is that the LinkedHashMap class (or one of its super classes) changed the implementation of putAll() so that it no longer calls put() to insert the new elements in the destination map. Hence, our own implementation is no longer able to catch this call to update the List at the same time a new key/value pair is added to the map. If you do similar things, you should check if there are such differences between the Java 7 and the Java 8 runtime which might affect your code. In general, this is a good example to Favor Composition over Inheritance in Java and Object Oriented Programming. The main problem is that the sub class depends on the behaviour of the superclass, and hence becomes fragile (and breaks when the superclass behaviour changes in an incompatible way). Collections are always a bad candidate for subclassing – instead, create a new class which implements the desired interfaces and then delegates to the necessary Collection classes. This is more effort at the beginning (to implement all the necessary interface methods), but makes the class much more resistent against modifications in the runtime classes. See also Effective Java, Second Edition: Item 16: Favor composition over inheritance.

Implicit default constructors

As you certainly know, if there is no constructor implemented in a class, the compiler implicitly synthesizes a default constructor. Hence, the following code compiles:

public class ClassA {

    public static void main(String[] args) {
        new ClassA();
    }
}

The disassembled code shows that there is indeed a constructor which essentially calls its super constructor, java.lang.Object.<init>:

$ javap -c ClassA.class
Compiled from "ClassA.java"
public class ClassA {
public ClassA();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #1                  // class ClassA
       3: invokespecial #16                 // Method "<init>":()V
       6: return
}

Now, there is one detail which even long-time Java developers are not always aware of: if any other constructor overload is added to the class, the compiler will not synthesize a default constructor – hence, the following code does not compile:

public class ClassA {

    public ClassA(int a) { }

    public static void main(String[] args) {
        new ClassA();
    }
}

ClassA.java:7: error: constructor ClassA in class ClassA cannot be applied to given types;
        new ClassA();
        ^
  required: int
  found: no arguments
  reason: actual and formal argument lists differ in length
1 error

This is in accordance to the Java Language Specification: 8.8.9. Default Constructor where it says:

If a class contains no constructor declarations, then a default constructor is implicitly declared.
...

Means, if there are any constructor declarations, then no default constructor will be created by the compiler. Note that there is also the same behavior in C++. Note also that the term “default constructor” as it is used by the language specification only refers to the parameterless constructor created by the compiler – hence, strictly spoken, a parameterless constructor which is explicitly implemented in the class is not a default constructor (even though developers often call it like that). Normally, this is not an issue, since the compiler will emit the above error when we try to instantiate the class through the default constructor. However, if the object is created dynamically (e.g. through reflection), there will be no compile time error – it will become a runtime error then:

public class ClassA {

    public ClassA(int a) { }

    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        Class clazz = Class.forName("ClassA");
        ClassA obj = (ClassA) clazz.newInstance();
    }
}

Exception in thread "main" java.lang.InstantiationException: ClassA
	at java.lang.Class.newInstance(Unknown Source)
	at ClassA.main(ClassA.java:9)
Caused by: java.lang.NoSuchMethodException: ClassA.<init>()
	at java.lang.Class.getConstructor0(Unknown Source)
	... 2 more
</init>

This can be important when using persistence frameworks which do these kind of things in the background. Other examples are dependency injection frameworks which might complain at the lack of default or empty constructor, if no constructor has the @Inject annotation.

JCA code sample: encrypting data with AES

The following code fragments show how a text can be encrypted and decrypted with the JCA (Java Cryptography Architecture) API using the AES cryptographic algorithm. Lets assume that we have a text which we want to encrypt, and also a key which we want to use for the encryption (remember that AES is a symmetric encryption algorithm, so the same key is used for both encryption and decryption):

String text = "Sample String which we want to encrypt";
String key = "Bar12345Bar12345";

The first thing we need to do is to get the cipher (the cryptographic algorithm) we want to use, and we also need to create a Key which represents our String-based key we defined above so that it can be used in JCE API calls. Cryptographic algorithms are normally working on binary data, so we also need to get the binary representation of the key first, using an appropriate character set:

Cipher cipher = Cipher.getInstance("AES");
Key aesKey = new SecretKeySpec(key.getBytes(StandardCharsets.ISO_8859_1), "AES");

The Key class has a method getEncoded() which returns the binary representation of the key as a byte[] array. We can use this method do print a hexadecimal dump of the key, and we will get the following output:

0000: 42 61 72 31 32 33 34 35 42 61 72 31 32 33 34 35  Bar12345Bar12345

Note that the key which we created is exactly 16 bytes = 128 bits long. This is the minimum key size supported by AES – and with a default JDK installation, we can not use larger keys. See … for more information how to use larger keys (192 bit and 256 bit). Next, we also need to get the binary representation of the text we want to encrypt, so that it can be fed into the encryption algorithm:

byte[] plaintext = text.getBytes(StandardCharsets.ISO_8859_1);

The dump of the plaintext data looks like this:

0000: 53 61 6D 70 6C 65 20 53 74 72 69 6E 67 20 77 68  Sample String wh
0010: 69 63 68 20 77 65 20 77 61 6E 74 20 74 6F 20 65  ich we want to e
0020: 6E 63 72 79 70 74                                ncrypt

Now, we can use the Ciper.doFinal() method to encrypt the whole plaintext:

cipher.init(Cipher.ENCRYPT_MODE, aesKey);
byte[] ciphertext = cipher.doFinal(plaintext);

The dump of the ciphertext (which is the encrypted data) looks like this:

0000: 29 0F 31 B5 7E D4 BD 02 69 8E C3 8E 87 C9 8A 4A  ).1µ~Ô½.i?Ã??É?J
0010: 2D C6 FB F9 E8 4E C9 F7 34 61 33 9B 46 27 57 49  -ÆûùèNÉ÷4a3?F'WI
0020: 31 44 53 5B 58 C6 1F 8A 99 A0 F5 18 5C EB 6A 05  1DS[XÆ.?? õ.\ëj.

One thing to mention is that the length of the ciphertext has been extended to a multiple of 128 bits – this is the block size which AES is using. Now we can use the ciphertext and decrypt it using the original key:

cipher.init(Cipher.DECRYPT_MODE, aesKey);
byte[] decrypted = cipher.doFinal(ciphertext);

The hexadecimal dump of the decrypted array shows that the data has been properly decrypted:

0000: 53 61 6D 70 6C 65 20 53 74 72 69 6E 67 20 77 68  Sample String wh
0010: 69 63 68 20 77 65 20 77 61 6E 74 20 74 6F 20 65  ich we want to e
0020: 6E 63 72 79 70 74                                ncrypt

The complete runnable sample is available at https://github.com/afester/CodeSamples/blob/master/Java/JCE/src/com/example/AESSample.java.

Executables contained in the Java JDK and JRE

New article: Executables within the JDK and JRE. We certainly know common executables like javac.exe and java.exe – but when looking into the bin directories of a JRE or JDK installation, there are a lot more executables. What is their purpose? This article lists and briefly describes the various executables which can be found in an Java JRE’s or JDK’s bin directory. For some of the more interesting tools, screenshots and simple usage examples are also shown. In addition, each tool is linked to its official documentation, as long as it is available.