JDK-6368038 : RFE subtyping final classes such as String, or Long to create compile time types
  • Type: Enhancement
  • Component: specification
  • Sub-Component: language
  • Affected Version: 6
  • Priority: P5
  • Status: Closed
  • Resolution: Not an Issue
  • OS: solaris_10
  • CPU: sparc
  • Submitted: 2006-01-02
  • Updated: 2011-02-16
  • Resolved: 2006-11-13
Related Reports
Relates :  
Description
A DESCRIPTION OF THE REQUEST :
Currently java does not allow sub-typing of final classes, such as String, Integer and Long. This Request for Enhancement is to allow in a limited way, sub-typing of final classes while not making any change that breaks java byte codes. Just as Enum's were a great improvement over simple int's, this would be a powerful step toward improving the development of more robust code.

The requirements, syntax and simple usage are described below.

1)	Allow 'logically' the definition of a new 'compile time' type by extending a final class. There is no actual new object type generated. Example:

	class SSN extends final String {}

	Note the use of the keyword 'final' after the word extends. This is what enables the extension, which otherwise the compiler would have refused. Objects declared as class SSN are actually 'String' objects and the byte codes generated are no different than if the word 'String' replaced all references of 'SSN'
in the source code. The advantage is in the compile time checking.
For example: a method which require both a 'Name' String and a 'SSN' String in the signature:

	Data getData(String p0, String p1) {��?��}

The compiler doesn't check for accidental reversal of the arguments since it doesn't know the difference between the name String and the SSN String. However, were the signature:

	Data getData(String p0, SSN p1) {��?��}

The compiler could have enforced correct usage. Conceptually this is very similar to the generics.

2)	Support for  'static final members' and 'static methods.' This would allow the developer to associate some basic functionality with this data type, but not change it fundamentally.
Example:

class SSN extends final String {

	public static final String SPECIAL_PREFIX = '000';

	public static String getPrefix(SSN ssn) { return first 3 digits ��?�� }
	public static String getMiddle(SSN ssn) { return middle 2 digits ��?�� }
	public static String getSuffix(SSN ssn) { return last 4 digits ��?�� }
}

Usage:
	String prefix = SSN.getPrefix(ssn);

Again no runtime byte code changes are necessary, this simply would generate an anonymous class, something like SSN$StaticHelper with each of these methods and the definition of any static constant values.

3)	Support for non static methods. While conceptually, it would appear that methods on the object were being called, this is just a syntactic sugar for the above form. All the non-static methods would simply be static methods on the generated anonymous class, so that when coding the developer can use a more nature object oriented syntax. Note: no method from the extended class can be overridden as there is no actual instance of this 'sub-type' variable. It is and always will be just its super type (here String).
Example:

class SSN extends final String {

	public static final String SPECIAL_PREFIX = '000';

	public String getPrefix() { return first 3 digits ��?�� }
	public String getMiddle() { return middle 2 digits ��?�� }
	public String getSuffix() { return last 4 digits ��?�� }
}

Usage:
	String prefix = ssn.getPrefix();

Behind the scenes this becomes the following in the anonymous class

class SSN$StaticHelper {

	public static final String SPECIAL_PREFIX = '000';

	public static String getPrefix(SSN this) { return first 3 digits ��?�� }
	public static String getMiddle(SSN this) { return middle 2 digits ��?�� }
	public static String getSuffix(SSN this) { return last 4 digits ��?�� }
}

4)	A further requirement is that a super type is always down cast-able  to 'any' known sub-type. This is because the developer may be constructing the super-type instance from raw data and need to cast it

5)	Another requirement would be that method overloading work as in:
class SomeClass {
	int getData(SSN ssn) { ��?�� }
	int getData(String str) { ��?�� }
}.

This is the only one in which I don't have a good idea as how it is done. But whatever generics do to separate something like this would be acceptable, when (Collection<int>) or (Collection<double>)
is used.

6)	Another requirement is that all the super-type constructors be available to the sub-type. In this example a SSN could be constructed from any constructor of a 'String'.

7)	The sub-type can be used anywhere in the program in place of the 'super-type' class. Therefore, in this example a SSN could be used directly for any method that accepts a 'String' as an argument, without the need for a (in this case) .toString() method call.

8)	What I DON'T recommend is the support for class member variables. As soon as you open that up, you need an actual instance and you no longer have a 'super class data-type'.

9)	Other examples would be for ID's which could be Strings or Int's or Longs. Geometry ID's come to mind where there are cell ID's, shape ID's, texture ID's, etc��?�� and if these had all been just int's and multiple were used in the sample method call signature, the only checking would occur in the runtime system when
the ID was determined to be of the wr


JUSTIFICATION :
I have found it very common in large systems to find IDs used in the system that are essentially integers or strings and are often used together in method arguments  but there is no compile time checking to ensure they are correct. This RFE provides a simple way to add both functionality and the compile time checking

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
They are listed in the requirements listed in the Description field. Since this is not a bug report, but instead an RFE, I'm not sure exactly how this field applies, unless you state: The JVM should support the following syntax or something akin to it to support sub-typing a final class.

class SSN extends final String {}
ACTUAL -
currently this is not supported and the closest thing to it is generics, but attemping to do this with generics results in a new object which is not transparent to the method being called as a parameter of the proper type.

---------- BEGIN SOURCE ----------
the SSN example in the description

class SSN extends final String {

	public static final String SPECIAL_PREFIX = '000';

	public String getPrefix() { return substring(0,3); }
	public String getMiddle() { return substring(4,5);  } // assumes a hypen
	public String getSuffix() { return substring(7,10); } // assumes a hypen


      public static final main(String argv[]) {
            SSN ssn = new SSN("123-45-6789");
             String pre = ssn.getPrefix();
            String mid = ssn.getMiddle();
            String suf = ssn.getSuffix();
            System.out.println("pre="+pre+" mid="+mid+" suf="+suf);
     }

}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
The only work around is to either rely on javadoc or create intermediate classes to wrap these final classes and the wrappers must have a "get as super- type" method so that they can be passed to any system methods with super-type arguments.

Comments
EVALUATION The Justification about integers and strings being used as IDs without compile-time checking would be better placed at the top of the request! This request adds a lot of new mechanisms and changes a lot of existing semantics, all to avoid defining simple classes that represent IDs. We are not going to modify 'final' for such a reason. For simple use cases, the ability to alias typenames might suffice, depending on how strict the compile-time checking is. (If SSN is an alias of String, can a method that accepts SSNs take Strings? The current CR would like the answer to be no; but for compatibility reasons, the answer might have to be yes.) This ability might appear in future. (The request for static members here are reminiscent of the proposals for static interface methods.)
13-11-2006