No compiler error about incompatible casts
Asked Answered
C

2

10

Sorry if this was already explained, but i didn't find similar threads anywhere in web.

Today I opened one project class in IDE and saw an error (red underline), though project was compiled successfully.

So, the code is:

public interface DatasourceImplementation<T extends Entity> {
     ....
}

public interface Datasource<T extends Entity> {
     ....
}


public interface DsContext {
    @Nullable
    <T extends Datasource> T get(String name);
}

And now we call this method like this:

DatasourceImplementation dsImpl = getDsContext().get("dsName");

Idea13 gives me error (incompatible types) - I think that's right.

Idea14 does not show any errors here.

JDK compiles it without errors - that's sad.

Must say, that in our project implementation class of A interface always implements B interface (possibly explains why Idea14 says it is OK), but in my opinion that can't justify this behaviour - because generally I can create class that implements A and doesn't implement B. I want static typization in my code, I do not want to see runtime class cast exceptions.

So, who's wrong here?

Upd. Add a screenshot with real classes (not sure it will explain something more, it's just the same as I described)

enter image description here

Confirmation answered 26/1, 2015 at 8:38 Comment(7)
This is indeed interesting, could we see a complete simplified program?Tripodic
@RichardTingle I added a screenshot of these 4 simplified classes - is that's enough?Confirmation
Post the code as text. First thought: you're using the raw type Datasource in you get() method generic return type.Broder
Can we see the line where you actually get it?Unbeatable
Agree that this is probably incorrect, but why returntype <T extends Datasource> and not just Datasource ? Much more likely to correctly compile with that, and likely it's what you actually mean. Or perhaps declare <T extends Datasource> as type parameter on your DsContext class instead of the get method, and have that as return type instead. E.g. public <T extends Datasource> DsContext() { ... } public T get() { ... }Maltose
@EvanKnowles this line is in the second code blockConfirmation
@Maltose The reason why method returns <T extends Datasource> is that we have specific interfaces like CollectionDatasource that extends Datasource and the goal was to get them from DsContext without cast.Confirmation
S
3

JDK is correct. The declaration promises to return ANY datasource, if it doesnt match there will be only a runtime error. Compiler may show some serious warnings but should compile it. The original developer of your snippet probably intended to avoid an explicit cast on each call.

Different ways on how to fix it depending on the intent:

  1. DataSource<?> get(String name): Caller will need to cast to DatasourceImplementation.
  2. <T extends Datasource> T get(Class<T> dsType, String name). The called function can check or select the returned type at runtime, e.g. wether to return Impl1 or Impl2.
  3. <T extends Entity>' Datasource<T> get(String name): This was probably intended. Works as long as DatasourceImplementation doesnt need to know the concrete entity-type. If it does need to know it, then <T extends Entity>' Datasource<T> get(Class<T> entityType, String name) would be better .
Sediment answered 26/1, 2015 at 9:59 Comment(3)
I understand what you say, but it sounds like using <T extends X> (where X is inteface) allows you to avoid any class casts between X and any Y interface. This is weird, don't you think?Confirmation
<T extends Number> T f() { return (T) new Integer(1); } : Compiler will only show the warning inside f(), but not when calling f(). Such a method with the return type not depending on any input parameter is indeed an ugly loophole. See https://mcmap.net/q/63513/-how-do-i-address-unchecked-cast-warnings for an ugly application.Sediment
OK, i wrote some tests that approved, that using <T extends Something> allow you to make any casts you want, since they are possible. It seems that Idea13 is wrong (answering my question). It should just mark code as unsafe.Confirmation
D
0

at a first glance your code / question seems to be a bit strange.

You have two interfaces independant of each other and both with a generic type.

  1. DatasourceImplementation<T extends Entity> and
  2. Datasource<T extends Entity>

However that you have <T extends Entity> does not mean that these bose T's are equal. In fact it can be completely different implementations (both extending from Entity) where none can be cast to the other type.

Furthermore you have your interface

public interface DsContext {
  @Nullable
  <T extends Datasource> T get(String name);
}

where you say that the get-method should return something which implements Datasource. However this T is completely independent to both of the other one T's. In fact the compiler should complain that you use the Datasource as raw type.

Did you mean <T extends Entity> Datasource<T> get(String name); instead?

However as there is no relationship from a Datasource to a DatasourceImplementation these are two types independent of each other, same as you would have a java.lang.String a java.lang.Number. Trying to assign a number to a reference declared of type String or vice versa would also result in a compiler error. Hence the compiler reporting an error seems to be perfectly fine.

Did the code fragments miss anything important (inheritance)? Furthermore did the compiler in all cases actually run?

Sebastian

Dendrochronology answered 5/2, 2015 at 12:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.