JDK-8157123 : java.util.Properties class is violating the Liskov's Substitution Principle (LSP)
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.util
  • Affected Version: 8,9
  • Priority: P4
  • Status: Closed
  • Resolution: Won't Fix
  • OS: generic
  • CPU: generic
  • Submitted: 2016-05-17
  • Updated: 2016-05-18
  • Resolved: 2016-05-18
Related Reports
Relates :  
Relates :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_65"
Java(TM) SE Runtime Environment (build 1.8.0_65-b17)
Java HotSpot(TM) 64-Bit Server VM (build 25.65-b01, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Windows 7 Professional 64 bit 

A DESCRIPTION OF THE PROBLEM :
The next example is how java.util.Properties extends java.util.Hashtable, which is wrong (a property file is not a hashtable). 

Because inherits from , the and methods can be applied to a object. Their use is strongly discouraged as they allow the caller to insert entries whose keys or values are not . The method should be used instead. If the or method is called on a "compromised" Properties object that contains a non- key or value, the call will fail. Similarly, the call to the or method will fail if it is called on a "compromised" Properties object that contains a non- key.  

****Here is the programming example1 :****
Properties extnNos = new Properties(); 
extnNos.put("Kathy", "3542");
extnNos.put("Joel", "4433");
// mistake in the following statement; 3224 instead of "3224"
extnNos.put("Joshua", 3224);
extnNos.list(System.out);

When executed, this program crashes: 

-- listing properties --
Kathy=3542
Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
at java.util.Properties.list(Properties.java:1050)
at UseProperties.main(UseProperties.java:10)


****Here is the programming example2 :****
Properties properties = new Properties();
properties.put("LOGNAME", "gsamarthyam");
System.out.println("LOGNAME=" + properties.getProperty("LOGNAME"));
Properties propertiesCopy = new Properties(properties);
System.out.println("LOGNAME=" + propertiesCopy.get("LOGNAME"));

This program prints: 

LOGNAME=gsamarthyam
LOGNAME=null

The output is clearly wrong. The get() method comes from the base class Hashtable and it isn't overridden in the derived class Properties. However, if we call the getProperty method, then the code works (see http://www.docjar.com/html/api/java/util/Properties.java.html). 

When we create a copy of a Properties file with the call new Properties(properties), the reference to original Properties is maintained in a "defaults" variable. When looking for a key by calling getProperty, this defaults.getProperty(key) is called when the super.get(key) returns null. 

The behaviour exhibited by Properties.java is clearly wrong/misleading. It is a much more deeper problem that can be root caused to the effects of violating LSP.

REGRESSION.  Last worked in version 8u92

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the following programe:

Properties properties = new Properties();
properties.put("LOGNAME", "gsamarthyam");
System.out.println("LOGNAME=" + properties.getProperty("LOGNAME"));
Properties propertiesCopy = new Properties(properties);
System.out.println("LOGNAME=" + propertiesCopy.get("LOGNAME"));


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
LOGNAME=gsamarthyam
LOGNAME=null
ACTUAL -
Not 100% sure about the actual result, please read description for more details
LOGNAME=gsamarthyam
LOGNAME=null

REPRODUCIBILITY :
This bug can be reproduced always.

CUSTOMER SUBMITTED WORKAROUND :
Use HashTable class as composition inside Properties class rather extending it. 


Comments
Having Properties extend Hashtable is an unfortunate, early design error that cannot be fixed compatibly at this point. This bug report points out some of the consequences of this error. The linked bugs mention others. If we were to redesign the Properties class today, it would be completely different, and most likely the design would adhere to LSP. Closing as Won't Fix.
18-05-2016

The Java API documentation for Properties talks about how the usage of put and putAll is discouraged, but the same about get method is missing. https://docs.oracle.com/javase/8/docs/api/java/util/Properties.html The get method from the super class(Hashtable) is not returning correct results when used with Properties object , either its use should be disallowed in properties class by throwing an exception, or the documentation should explicitly mention that get is not supported. Attached test case executed on: JDK 8u92 - Fail JDK 9ea - Fail
17-05-2016