Moses Besong
Moses Besong

Reputation: 65

Java Narrowing Reference Conversion from a type to an interface

Trying to understand java narrowing conversion from a class to an interface. The JLS(JLS-5.1.6) states:

From any class type C to any non-parameterized interface type K, provided that C is not final and does not implement K.

To test this, I created a class and an interface. Then tried casting the class to the interface, but get a run-time ClassCastException. This is a sample of my code.

class NarrowingReferenceConversion
{
    public static void main(String args[])
    {

        S s = new S();
        T t = (T)s;

    }
}

interface T
{
    public void print();
}

class S
{
    public void print(){
        System.out.println("S.print()");
    }
}

On compiling and running the above I get the following error message:

Exception in thread "main" java.lang.ClassCastException: S cannot be cast to T

Upvotes: 2

Views: 571

Answers (2)

GhostCat
GhostCat

Reputation: 140427

The simple thing is:

 S s = new S();
 T t = (T)s;

Given the current code you are showing, the compiler could know that this cast doesn't make sense, and must fail at runtime.

But thing is: your case here is a pretty specific example. The common use case is less "clear". As shown by Eran, it is pretty simple to construct a similar example where the cast at runtime might work, or not, depending on very subtle differences.

So, the pragmatic answer is: the fact that a compiler could know that a program is invalid and will fail later on doesn't necessarily cause a compiler to fail.

In other words: when you design languages, and construct compilers there are always trade offs to make. As in: sometimes it is just not worth adding a very specific check at compile time. You rather accept that a more generic rule that might result in failures at runtime instead of compile time.

Upvotes: 1

Eran
Eran

Reputation: 393781

This is a conversion that is not guaranteed to work, just like casting a reference of a base class to a sub-class is not guaranteed to work. That's why it is considered a narrowing conversion.

The compiler knows that the conversion might work at runtime, so it allows it, but if it doesn't work, ClassCastException is thrown at runtime.

Only if you assign to s an instance of a sub-class of S that does implement the interface T, the conversion will work.

class NarrowingReferenceConversion
{
    public static void main(String args[])
    {

        S s = new S2();
        T t = (T) s; // this will work, since S2 implements T

    }
}

interface T
{
    public void print();
}

class S
{
    public void print(){
        System.out.println("S.print()");
    }
}

class S2 extends S implements T
{
}

Let's explain the two conditions of this conversion:

  1. "C is not final" - if it was final, there would be no sub-classes of C, so the compiler knows for sure this conversion can never work, and compilation fails.

  2. "does not implement K" - if C implements K, this is no longer a narrowing conversion. It becomes a Widening Reference Conversion, guaranteed to succeed at runtime. In fact, there would be no need to use the cast operator. A simple assignment will do.

Upvotes: 3

Related Questions