0

I have 2 abstract classes that are extended, and another class that uses them:

-----Base-----

public abstract class HttpRequest<RESPONSE_TYPE extends HttpResponse>
{...}
public abstract class HttpResponse<DATA_TYPE>
{...}

-----Extended-----

public class MockyTextRequest extends HttpRequest<MockyTextResponse>
{...}
public class MockyTextResponse extends HttpResponse<TextWithTitle>
{...}

----Utility------

public class HttpClient
{...
    public <REQUEST_TYPE extends HttpRequest<RESPONSE_TYPE>, RESPONSE_TYPE extends HttpResponse<RESULT_TYPE>, RESULT_TYPE> RESPONSE_TYPE synchronicRequest(@NonNull final REQUEST_TYPE httpRequest, @Nullable final Object tag) throws IOException, HttpException, ParseException
    {...}
...}

When I compile, I get:

error: incompatible types: inference variable RESPONSE_TYPE has incompatible upper bounds HttpResponse,MockyTextResponse

on

MockyTextResponse httpResponse = HttpClient.get().synchronicRequest(new MockyTextRequest(), null);

Any Ideas?


Stange fact, this call

public <REQUEST_TYPE extends HttpRequest<RESPONSE_TYPE>, RESPONSE_TYPE extends HttpResponse<RESULT_TYPE>, RESULT_TYPE> void request(@NonNull final REQUEST_TYPE httpRequest, @Nullable final Object tag, @Nullable final Listener<REQUEST_TYPE, RESPONSE_TYPE> listener)
{...}

called

HttpClient.get().request(new MockyTextRequest(), MainActivity.this);

Works ok

Full code here in github

Cheborra
  • 2,627
  • 4
  • 23
  • 39
  • Do you actually need `RESULT_TYPE` inside `sychronicRequest`? Btw what version of Java are you compiling on? – Radiodef Apr 12 '15 at 20:57
  • It's Java 7. I do use RESULT_TYPE inside the function to avoid a cast – Cheborra Apr 12 '15 at 21:20
  • Well, I might be able to come up with a clever solution but I would say don't hold your breath. This looks like an inference bug, Java 7 was rife with small things like this. – Radiodef Apr 12 '15 at 21:24

1 Answers1

2

The inference algorithm seems to not be able to infer the type of RESPONSE_TYPE. It looks like all three type parameters should be given by MockyTextRequest.

I'm not exactly sure why this is the case with respect to the specification but it has to do with the "nesting".

The error can be reproduced in the following way:

// same error on this line
// C (String) is not inferred
m(new ArrayList<List<String>>());

// the compiler is able to infer B from A
// but not C from A
static <A extends List<B>, B extends List<C>, C> void m(A a) {}

This is the kind of thing that's been fixed in Java 8:

You could provide a witness:

MockyTextResponse httpResponse =
    HttpClient.get().
        <MockyTextRequest, MockyTextResponse, TextWithTitle>
        synchronicRequest(new MockyTextRequest(), null);

Or a few ways to loosen up the type parameters:

1.

public <
    RESPONSE_TYPE extends HttpResponse<RESULT_TYPE>,
    RESULT_TYPE
> RESPONSE_TYPE synchronicRequest(
    final HttpRequest<RESPONSE_TYPE> httpRequest, final Object tag)

2.

public <
    REQUEST_TYPE extends HttpRequest<RESPONSE_TYPE>,
    RESPONSE_TYPE extends HttpResponse<?>
> RESPONSE_TYPE synchronicRequest(
    final REQUEST_TYPE httpRequest, final Object tag)

3.

public <
    REQUEST_TYPE extends HttpRequest<RESPONSE_TYPE>,
    RESPONSE_TYPE extends HttpResponse<? extends RESULT_TYPE>,
    RESULT_TYPE
> RESPONSE_TYPE synchronicRequest(
    final REQUEST_TYPE httpRequest, final Object tag)

From your descriptions, it seems like #1 is the best. (#2 and #3 are basically the same because RESULT_TYPE doesn't get inferred anyway.)


Another way would be to define HttpRequest like the following:

class HttpRequest<
    RESPONSE_TYPE extends HttpResponse<RESULT_TYPE>,
    RESULT_TYPE
>

And then:

class MockyTextRequest
extends HttpRequest<MockyTextResponse, TextWithTitle>

public <
    REQUEST_TYPE extends HttpRequest<RESPONSE_TYPE, RESULT_TYPE>,
    RESPONSE_TYPE extends HttpResponse<RESULT_TYPE>,
    RESULT_TYPE
> RESPONSE_TYPE synchronicRequest(
    final REQUEST_TYPE httpRequest, final Object tag)

But this probably makes some of your code a lot more verbose.


As a side note, you should avoid raw types when you can:

//                                                  vvv
class HttpRequest<RESPONSE_TYPE extends HttpResponse<?>>
Community
  • 1
  • 1
Radiodef
  • 37,180
  • 14
  • 90
  • 125
  • Well, using "...RESPONSE_TYPE extends HttpResponse..." instead does work, but forces me to cast some vars inside the body. – Cheborra Apr 12 '15 at 21:22
  • Strange thing is, another almost-the-same function does work (see my edit) – Cheborra Apr 12 '15 at 21:23
  • Yes the one in your edit works because `RESULT_TYPE` can be inferred from the argument. See my intro comments about the nesting. – Radiodef Apr 12 '15 at 21:26
  • It is called int almost the exact same way "HttpClient.get().request(new MockyTextRequest(), MainActivity.this);" – Cheborra Apr 12 '15 at 21:31
  • But you're passing a `Listener` to the method, where `REQUEST_TYPE` is not "nested" in another inferred type. – Radiodef Apr 12 '15 at 21:37