JDK-6526446 : Fixes to JLS5.5 Casting Conversion
  • Type: Bug
  • Component: specification
  • Sub-Component: language
  • Affected Version: 7
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2007-02-19
  • Updated: 2014-02-26
  • Resolved: 2011-07-21
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 rcFixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description
* Casting contexts allow the use of ***one of the following***:
- a widening and narrowing primitive conversion (5.1.4)
    // Missing; reported by Neal and J.Stephen Adamcyzk
- a boxing conversion (5.1.7) optionally followed by a widening reference conversion (5.1.5)
    // Extension of existing clause
- an unboxing conversion (5.1.8) optionally followed by a widening primitive conversion (5.1.2)
    // Extension of existing clause
- a widening reference conversion (5.1.5) optionally followed by either an unboxing conversion (5.1.8) or an unchecked conversion (5.1.9)
    // Extension of existing clause, as per 6558543
- a narrowing reference conversion (5.1.6) optionally followed by either an unboxing conversion (5.1.8) or an unchecked conversion (5.1.9)
    // Extension of existing clause, as per 6558543

(The detailed rules for casting must also change to reflect the updated list.)


* A cast from a type S to a type variable T is unchecked unless S<:T
(Not "A cast to a type variable is always unchecked.")


* If S is an interface type:
    If T is an array type, then a compile-time error occurs unless S is the type java.io.Serializable or the type Cloneable, the only interfaces implemented by arrays.
    If T is a type that is final, then:
      If S is not a parameterized type or a raw type, then T must implement S, or a compile-time error occurs.


When the source type is an intersection type S = A1 & A2 & ... & An, the success of the cast should be determined by the most restrictive component of the intersection type. That is, if there exists an Ai (i:1..n) which is not convertible into the target type T, then the cast should fail. (Corresponds to compiler CR 6557182; see comments therein.)

Josh reported a problem where highlights the lack of cast conversion rules for nested types:

class Outer<E> {
    Inner inner;
    Outer(E e) { inner = new Inner(e); }

    class Inner {
        E e;
        Inner(E e) { this.e = e; }
        E getOtherElement(Object other) {
            Inner that = (Inner) other; // Shouldn't this generate a warning?
            return that.e;
        }
    }
    public static void main(String[] args) {
        Outer<String> s  = new Outer<String>("hovercraft");
        Outer<Integer> i = new Outer<Integer>(1234);
        // The next line produces a ClastCastException at runtime!
        String producesClassCast = s.inner.getOtherElement(i.inner);
    }
}

It compiles, as of javac 1.6.0_06, without generating a warning, and the compiler-generated cast fails at runtime for:
 
    String producesClassCast = s.inner.getOtherElement(i.inner);
 
This appears to violate Java's "cast iron guarantee." Luckily, the compiler is simply failing to generate an unchecked cast warning on line 16:
 
    Inner that = (Inner) other; // Shouldn't this generate a warning?

The problem is that Inner (in this context) refers to Outer<E>.Inner, so we're casting from Object to a parameterized type. JLS clarification is required. javac in JDK7 rightly gives a warning:

Test.java:12: warning: [unchecked] unchecked cast
found   : java.lang.Object
required: Outer<E>.Inner
           Inner that = (Inner) other; // Shouldn't this generate awarning?
                                ^

Comments
EVALUATION javac also allows, in both assignment and casting contexts, the combination widening+unboxing+widening: <T extends Short> void foo(T arg) { short s1 = arg; short s2 = (short) arg; long l1 = arg; long l2 = (long) arg; }
04-02-2011

EVALUATION With regard to the second Description on nested generic types, Maurizio discovered that other compilers (most notably Eclipse) apply the following simple rule of thumb: Given a type S==A1<U1>.B1<V1> and a type T==A2<U2>.B2<V2> there exist a cast conversion from S to T iff: *) B2<V2> is castable to B1<V1> AND *) A2<U2> is castable to A1<U1> That is, we use the same (non-JLS driven) rules that we apply for subtyping involving qualified types. Another possibility would be to define cast conversion from S to T iff: *) |S| <: |T| or |T| <: |S| AND *) {U2, V2} <= {U1, V1} or {U1, V1} <= {U2, V2} That is, the two types should be in some subtyping relation AND there type-containment relation should be defined for their type parameters - this is equivalent to considering the qualified type as a non-nested type with an extended set of type variables (type variables from 'outer' + type variables from 'inner'). This should be easier to formalize in the JLS.
27-08-2009

EVALUATION OK to the fixes in the first Description.
19-02-2007