7
interface I {
}
class A {
}
class B {
}
public class Test {
    public static void main(String args[]) {
        A a = null;
        B b = (B)a; // error: inconvertible types

        I i = null;
        B b1 = (B)i;
    }
}

i know why a can not be cast to B, because of B is not inherit from A.
my question is about, why B b1 = (B)i; is allowed since B is not implements from I ?
and why B b1 = (B)i; this line will not force a runtime exception since i is null?

Kalhan.Toress
  • 21,683
  • 8
  • 68
  • 92

6 Answers6

11

The compiler doesn't know everything about your class hierarchy. As far as it's concerned, there could be class B2 extends B implements I, in which case the above cast would need to work.

Max
  • 21,123
  • 5
  • 49
  • 71
Jeffrey
  • 44,417
  • 8
  • 90
  • 141
  • 1
    Not the downvoter, but... isn't it the compiler's job to know the class hierarchy of the objects its compiling against? – Powerlord Apr 02 '14 at 15:16
  • 3
    @Powerlord That's impossible. It could be compiling `Test` before you've even written the class that will extend `B`. Java 8 has a lot of cool new features, but time travel is not one of them. – ajb Apr 02 '14 at 15:17
  • @ajb: how is that different from compiling `Test` before compiling `A` and `B`? Why does it have the knowledge that these *are* connected and the former isn't? – Jeroen Vannevel Apr 02 '14 at 15:19
  • I'm very curious as well as to why the one situation can be caught and the other can't. My guess right now would simply be that the compiler only does it for classes and not for interfaces, but I don't see why it would restrict itself to that. – Jeroen Vannevel Apr 02 '14 at 15:19
  • @JeroenVannevel Good question... my comment may have been a little too glib. But see my answer, which I think explains why it can't be determined until run-time whether the cast could possibly succeed or not. The language will only flag an error if the cast could never possibly succeed. And you really can't have a rule in Java that says "look at the entire class hierarchy to see if there are any classes _anywhere_ in your program that extend `B` and implement `I`". For one thing, classes can be loaded dynamically. – ajb Apr 02 '14 at 15:27
  • 3
    @JeroenVannevel The compiler knows here that `A` and `B` are not related, and since Java doesn't do multiple inheritance of classes it can be 100% sure that no subclass of `B` can also be a subclass of `A`. That doesn't hold with interfaces, since it _is_ possible for the same class to be both a subclass of `B` and an implementation of `I` (unless `B` were `final`). – Ian Roberts Apr 02 '14 at 15:37
  • @IanRoberts: I have expanded upon this in an answer, let me know if I have overlooked something blatantly wrong. http://stackoverflow.com/a/22816630/1864167 – Jeroen Vannevel Apr 02 '14 at 15:47
  • can you please check the last part of my updated question ? – Kalhan.Toress Apr 02 '14 at 19:40
  • @KalhanoToressPamuditha http://stackoverflow.com/a/18723704/758280 – Jeffrey Apr 02 '14 at 20:11
3

it is a Narrowing Reference Conversion.. according to java spec $5.1.5 following is allowed.

From any interface type J to any non-parameterized class type C that is not final.

this conversion will not show any compile time error but will be checked at run time. it would throw a ClassCastException according to $5.5.3 if the interface was initialized with a proper class which is not related to B. but it is not throwing now as the interface is null and as we know

A value of the null type (the null reference is the only such value) may be assigned to any reference type, resulting in a null reference of that type.

N.B. .. i was not acknowledged about all the above terms... I just feel interest to search after seeing the question. so please correct me if my finding is wrong or i misunderstood anything

stinepike
  • 54,068
  • 14
  • 92
  • 112
  • -1 because the information is wrong. I think you're saying that if `i` were initialized to something that is non-null, the program would raise a `ClassCastException`. But that is not necessarily true. – ajb Apr 02 '14 at 15:34
  • why sir? i was checking like I i = new I(){} – stinepike Apr 02 '14 at 15:36
  • Try it with `I i = new B2()` where `B2` is a class that extends `B` and implements `I`. – ajb Apr 02 '14 at 15:37
  • i think you misunderstood me .. if B is already a superclass of B2 then how do you expect to get class cast exception??? it is not irrevelant then ... if there is no relation between I and B then it will throw that exception .. sorry sir, I couldn't agree with your downvote – stinepike Apr 02 '14 at 15:40
  • Have you tried it? I can write a program where `i` is not null and `(B)i` does **not** throw a `ClassCastException`, but your answer seems to imply this is impossible. – ajb Apr 02 '14 at 15:46
  • if you read the question carefullly it is saying that why it is happening when there is no relation between I and B .. but you are telling a case where I and B is related .. if you have any misunderstanding for my writting .. I am editing a bit – stinepike Apr 02 '14 at 15:48
  • I think you can try by initializing the I using any other class unrelated to B.. like I i = new C(); ... where C only implements I then check .. thanks – stinepike Apr 02 '14 at 15:49
  • My downvote was based on the answer you posted before you edited it. Now that it appears you've corrected the misinformation, I've removed it. – ajb Apr 02 '14 at 15:57
  • thanks @ajb.. and I am sorry that my previous part created the misunderstanding .. thanks for helping me out – stinepike Apr 02 '14 at 15:58
3

When the compiler comes across B b = (B)a; it knows without doubt that a is type A and it does not legally cast to a B.

However, when the compiler is compiling B b1 = (B)i; it only knows that i is an object that implements I - it could be:

class C extends B implements I {
}

and so the cast to B could be legal so it cannot treat it as an error.

This is really just a re-statement of Jaffrey's answer with a little more narrative.

Community
  • 1
  • 1
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
2

Consider:

interface I { }
class A { }
class B { }
class A2 extends A implements I { }
class B2 extends B implements I { }

void castToB(I i) {
    B b1 = (B)i;  // (HERE)
}

void foo() {
    A2 a2 = new A2();
    castToB(a2);  // legal, since a2 implements I, but the cast to B will throw an
                  // exception at runtime
    B2 b2 = new B2();
    castToB(b2);  // legal, since b2 implements I; the cast to B now succeeds, since
                  // a B2 is a B
}

This shows why the cast in the line marked HERE must be legal, because there are scenarios in which it can succeed. Java would flag the cast as an error only if there's absolutely no possible way it could succeed.

ajb
  • 31,309
  • 3
  • 58
  • 84
  • How does this answer anything? You've completely disregarded the original question. – Jeroen Vannevel Apr 02 '14 at 15:24
  • @JeroenVannevel No, I didn't, but I thought it would be clear how it related to the question. I guess it wasn't. I've added some clarification. – ajb Apr 02 '14 at 15:30
1

I believe the other answers are missing one important detail: it is possible because the compiler simply doesn't bother looking into interfaces.

The arguments that are supposed to explain this have been phrased as

there could be class B2 extends B implements I

and

The compiler knows here that A and B are not related

These are very true, but it doesn't explain why the compiler knows that these two classes aren't related and not why it can't know if an interface has been implemented.

In the end, these two situations should be rather similar: if it can check at compile time if two classes have a relation, then it should be able to check at compile time if an interface is implemented in that class since both of these situations have their information available at compile time.

Therefore I agree with the comment of Hot Licks in the duplicate:

Because. Even though Cat may not directly implement Furniture, some superclass of Cat may. And the compiler chooses to not dig into such ugly details, since the rules for interfaces are pretty convoluted. (Thank you, Sun.) And there is no general rule requiring the compiler to detect an inevitable runtime exception (such as divide by zero) -- it's more of a "service" it provides.

Checking the interface at compile time should be possible, it's just not done by the compiler for reasons that we can only guess at. that are discussed in the comments.

After an extensive (and very interesting) discussion in the comments below, I believe this has been cleared up.

It is easy and cheap to check if two classes have a relationship, you just look at both their hierarchies. Interfaces on the other hand, require you to have knowledge about all classes and interfaces in the project (including external resources like libraries) in order to determine if there is a relationship between the interface and the class.

This leads me to the conclusion that while it is theoretically possible, it's just not feasible because it would require two things:

  1. Recompiling your source code and all included libraries.
  2. Very expensive checks that have to go through the hierarchies of each class.
Community
  • 1
  • 1
Jeroen Vannevel
  • 43,651
  • 22
  • 107
  • 170
  • I still disagree. Are you saying that the compiler can check whether `I` is implemented by some subclass of `B` somewhere? If so, how can the compiler check whether a class could be loaded with `Class.forName()` that extends `B` and implements `I`? – ajb Apr 02 '14 at 15:54
  • The compiler has knowledge over all classes and interfaces, why shouldn't it be able to see if there's a subclass that might implement `I`? I'll agree that it might be a rather expensive check and that this might be the reason that it isn't done, but all the information is there, the compiler just has to take a look at it. – Jeroen Vannevel Apr 02 '14 at 15:58
  • Are you sure the compiler has knowledge about *all* classes? Can't `Class.forName()` be used at run time to load a class that wasn't yet written at the time the source was compiled--possibly even on a totally different computer? What if the above code is in a `.jar` in an open-source library that someone can download and use together with their own classes--surely, when the open-source author compiles the source, the compiler can't look on every computer in the universe? – ajb Apr 02 '14 at 16:02
  • I agree that there might be some class added at runtime or in another library that would make this conversion possible, but are classes unknown at compile time really something the compiler should keep in mind? And is a class that would connect an otherwise illegal expression at runtime really the kind of programming that should be supported? – Jeroen Vannevel Apr 02 '14 at 16:13
  • For classes the compiler doesn't need to "look at everything" - the rules of Java are such that if you know that a class `B` does not extend `A` then you _know_ that no subclass of `B` can ever extend `A` either. You don't need to look at any other classes to know this, it's implicit in the fact that every class (except `java.lang.Object`) has exactly one superclass. – Ian Roberts Apr 02 '14 at 16:15
  • "Are classes unknown at compile time something the compiler should keep in mind?" Yes. Isn't this kind of extensibility one of the important reasons for OOP in the first place? – ajb Apr 02 '14 at 16:21
  • @IanRoberts: fair enough, that would mean that it is very cheap to check whether or not such a conversion is possible. Would you agree with the reasoning that the same isn't done for interfaces because it would be too expensive? Interfaces have the requirement that all object hierarchies should be known, after which they have to check whether either of them extend `B` and if it does, if it also implements `I`. – Jeroen Vannevel Apr 02 '14 at 16:24
  • @ajb: OOP definitely encourages extensibility but I have my doubts whether or not that extends to the runtime as well. Compiled languages have some applications for runtime aspects but it still curses with their very notion of being a compiled language. Whether or not OOP is being applied doesn't matter much, I think. – Jeroen Vannevel Apr 02 '14 at 16:28
  • I'm not sure that the problem is just extensibility at run time. The biggest problem I see is where someone compiles a library L and distributes it, and someone else writes a program P that uses the classes in L. If the legality of source code in L depends on what classes may or may not exist in P, how would that work? Unless you say that in order to build P, the compiler must recompile all the source code in L. Even if that were feasible, the person writing L would still want to compile their source before distributing it--should the cast be legal or not when L is first compiled? – ajb Apr 02 '14 at 16:34
  • @ajb: that cleared it up for me. I have edited my post to account for the remarks by you and Ian, have I missed something? I left out the actual runtime aspect because I strongly doubt whether that is something the compiler should worry about, but your remark on the external libraries does make a lot of sense. – Jeroen Vannevel Apr 02 '14 at 16:56
-1

Here is some good information on Upcasting and Down casting. http://forum.codecall.net/topic/50451-upcasting-downcasting/

Because every Class inherits from Object you can always Upcast to Object and Downcast to some other Type (B)(Object)a. Not that this is a good idea but it is valid. If you had actually instantiated A you would get a runtime exception java.lang.ClassCastException the same as if you instantiated and instance of I i = new I(){}

interface I {
}
class A {
}
class B {
}
class Test {
    public static void main(String args[]) {
        A a = null;
        B b = (B)(Object)a;

        I i = null;
        B b1 = (B)i;
    }
}
Scott
  • 1,648
  • 13
  • 21