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

AdamSkywalker
  • 10,723
  • 2
  • 33
  • 71
  • 2
    This is indeed interesting, could we see a complete simplified program? – Richard Tingle Jan 26 '15 at 08:41
  • @RichardTingle I added a screenshot of these 4 simplified classes - is that's enough? – AdamSkywalker Jan 26 '15 at 08:51
  • Post the code as text. First thought: you're using the raw type Datasource in you `get()` method generic return type. – JB Nizet Jan 26 '15 at 08:53
  • Can we see the line where you actually `get` it? – Evan Knowles Jan 26 '15 at 09:00
  • 1
    Agree that this is probably incorrect, but why returntype `` and not just `Datasource` ? Much more likely to correctly compile with that, and likely it's what you actually mean. Or perhaps declare `` as type parameter on your DsContext class instead of the `get` method, and have that as return type instead. E.g. `public DsContext() { ... } public T get() { ... }` – geert3 Jan 26 '15 at 09:09
  • @EvanKnowles this line is in the second code block – AdamSkywalker Jan 26 '15 at 09:43
  • @geert3 The reason why method returns is that we have specific interfaces like CollectionDatasource that extends Datasource and the goal was to get them from DsContext without cast. – AdamSkywalker Jan 26 '15 at 09:44

2 Answers2

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 .
Markus Kull
  • 1,408
  • 12
  • 15
  • 1
    I understand what you say, but it sounds like using (where X is inteface) allows you to avoid any class casts between X and any Y interface. This is weird, don't you think? – AdamSkywalker Jan 26 '15 at 11:22
  • ` 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 http://stackoverflow.com/a/509324/404501 for an ugly application. – Markus Kull Jan 26 '15 at 12:11
  • OK, i wrote some tests that approved, that using 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. – AdamSkywalker Feb 08 '15 at 14:28
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

Sebastian
  • 374
  • 2
  • 7