2

Example:

interface S {}

interface SS extends S {}

abstract class A<T extends S> {
    T get() {…}
}

abstract class B<BT extends SS> extends A<BT> {}

Why does ((B)someInstanceOfB).get() return an object of type S (and we should cast it to SS manually), when the compiler could determine that returned object is at least of type SS?

Why doesn't the compiler make implicit class cast to have clearer code? The code is of version 1.5+ and this is not secret for compiler. (Solved)

Update: Why doesn't the compiler compile B class as it implicitly has method BT get() { return super.get(); } ?

Is this problem solved in Java 1.7+?

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
Timofey Gorshkov
  • 4,987
  • 6
  • 41
  • 66

2 Answers2

4

By casting to B you're using a raw type.

Using a raw type removes all generic information from the object, no matter where it is.

That means that for the raw type B the get method looks like it is returning a S (because that's the erasure (i.e. the actual type used at runtime) of the type parameter T).

To avoid this, never use raw types! They are meant exclusively for backwards compatibility and interaction with legacy code.

Cast to B<? extends SS> instead.

And no: this problem will probably never be "solved" in any future version of Java, as it's non-existing when you use generics correctly.

Regarding Update: no, B does not have a method BT get(). It has a method T get() where T is bound to the type parameter BT which has a lower bound SS. But since all generic type information is discarded when you use a raw type, T will still fall back to the original erasure, which is S.

The rule is quite simple and basically says "when you use a raw type it acts as if the class has no generics at all". The implications, however, are not always obvious, as in this case.

Community
  • 1
  • 1
Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • I get you. It is made for backward compatibility of compiled classes: to have possibility to use compiled code of new versions with obsolete code and compilers. – Timofey Gorshkov Apr 16 '13 at 12:28
  • Some thoughts about question update. It was a rhetorical question. I understand that, till compiler logic proposed by me doesn't implemented (defined by standard) in 1.5v compiler, it will be never implemented. As for me, cast to `B extends SS>` corrupts aesthetic of code, and I have been preferred some more bytes in compiled class (implicit method proposed by me). – Timofey Gorshkov Apr 16 '13 at 12:51
  • 2
    Well, using `B` corrupts the *logic* of the code. If this is such an important issue for you (and you don't mind all the "raw type" warnings), then you can just add a manual method `BT get()` into your `B` class an achieve the same effect. The logic of how Java handles raw types is *very unlikely* to change significantly, for the reasons I stated. And if *you* prefer a few more bytes, that goal might very well conflict with other goals of *other people* (for example what if the method `get` was added to `A` **after** your class `B` was compiled?). – Joachim Sauer Apr 16 '13 at 12:53
  • Also: if it's a rhetorical question, then why make an edit and even draw my focus to that edit? That makes it look like you want an answer. – Joachim Sauer Apr 16 '13 at 12:54
  • I liked to read your thoughs. And case proposed by you _(“what if the method `get` was added to `A` after your class `B` was compiled?”)_ is really important reason why it is so. – Timofey Gorshkov Apr 16 '13 at 13:14
1

You didn't parameterize your instance of B.

Let's say you have the class C implementing SS:

Then, new B<C>().get(); would return an object of type C.

On the new B().get(); line, your IDE must have told you "B is a raw type. References to generic type B should be parameterized.".

sp00m
  • 47,968
  • 31
  • 142
  • 252
  • 1
    @Errandir The problem remains the same: using `(B) someInstanceOfB`, you don't parameterize `B`. You have to use `(B) someInstanceOfB`, or `(B>) someInstanceOfB` at least (`get` will then return an `Object`). – sp00m Apr 16 '13 at 12:08
  • There is something wrong in your argumentation. Code chunks `S s = ((B)someInstanceOfB).get()` and `S s = ((A)someInstanceOfA).get()` will work fine. – Timofey Gorshkov Apr 16 '13 at 12:16
  • We have partially parametrized `A`. – Timofey Gorshkov Apr 16 '13 at 12:17