Weird method in EventBusImpl.java, Vertx.3

78 views
Skip to first unread message

Matthew Huang

unread,
Oct 22, 2015, 2:43:41 PM10/22/15
to vert.x
I am refactoring our EasyMock based test cases to use Vertx.3.  Specifically, I am refactoring the code that used to use registerHandler (......) to use consumer(......) method now.

All is good until I encounter the below method.

@Override
public <T> MessageConsumer<T> consumer(String address) {
Objects.requireNonNull(address, "address");
return new HandlerRegistration<>(address, false, false, -1);
}

The below chunk won't compile.

......
MessageConsumer<JsonObject> consumer = createMock(MessageConsumer.class);
expect(eventBus.consumer("address"))andReturn(consumer)times(1);
......


The compilation error message is as below:
andReturn (io.vertx.core.eventbus.MessageConsumer<java.lang.Object>) in IExpectationSetters cannot be applied to (io.vertx.core.eventbus.MessageConsumer<io.vertx.core.json.JsonObject>)

The problem is that the return type of the consumer method is not really maintained. 

I consider this ill-typed method a defect in Vertx (EasyMock just exposes this issue expliicitly). 

Can someone in Vertx team have a look on this or explain the design goal of this method and guide me what should do in mock tests to get around it?

Thanks,
Matthew

Tim Fox

unread,
Oct 22, 2015, 2:47:47 PM10/22/15
to ve...@googlegroups.com
Can you explain what you mean by "ill typed method"?
--
You received this message because you are subscribed to the Google Groups "vert.x" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vertx+un...@googlegroups.com.
Visit this group at http://groups.google.com/group/vertx.
To view this discussion on the web, visit https://groups.google.com/d/msgid/vertx/04ecfb90-bdae-4cfe-ad81-cd29b426cf7b%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Matthew Huang

unread,
Oct 22, 2015, 2:54:11 PM10/22/15
to vert.x
Maybe I am not using the appropriate term as the type signature of this method is fine. What I should say is that the method signature requires a generic return type T but that type T is not honored by the method. The same thing also happens to 

@Override
public <T> MessageConsumer<T> localConsumer(String address) {
Objects.requireNonNull(address, "address");
return new HandlerRegistration<>(address, false, true, -1);
}

This makes the compiler always uses the lowest possible type bound which is java.lang.Object. 

A good method (in the same class) is as below:

@Override
public <T> MessageConsumer<T> consumer(String address, Handler<Message<T>> handler) {
Objects.requireNonNull(handler, "handler");
MessageConsumer<T> consumer = consumer(address);
consumer.handler(handler);
return consumer;
}

in which the handler contains the type T and that type is captured inside the method and returned as expected.

Matthew

Julien Viet

unread,
Oct 23, 2015, 9:17:49 AM10/23/15
to ve...@googlegroups.com, Matthew Huang
Hi,

you need to write:

expect(eventBus.consumer<JsonObject>("address”))

to make it compile.

-- 
Julien Viet
www.julienviet.com

Julien Viet

unread,
Oct 23, 2015, 9:18:39 AM10/23/15
to ve...@googlegroups.com, Matthew Huang
Actually:

expect(eventBus.<JsonObject>consumer("address”))

Matthew Huang

unread,
Oct 23, 2015, 12:53:56 PM10/23/15
to vert.x, matthewh...@gmail.com
Thanks Julien, that's actually what I am using currently to get the thing compiled. But I still have the question. I guess my question should be rephrased as "What are the purpose of the method?"

@Override
public <T> MessageConsumer<T> consumer(String address) {
Objects.requireNonNull(address, "address");
return new HandlerRegistration<>(address, false, false, -1);
}

It appears to me the method relies on the caller to define what type it should return and this looks odd to me as the API user may use a wrong type and things will still compile but then runtime error will rise......

Matthew

Julien Viet

unread,
Oct 23, 2015, 1:54:22 PM10/23/15
to ve...@googlegroups.com, Matthew Huang, matthewh...@gmail.com
the purpose is to provide a free cast, pretty much like : Collection.<String>emptyList();

I agree it is possible to have runtime errors due to the lack of reified generics in Java.

So we rely on the fact that the user know the type of objects that will be sent to the consumer address, in case of doubt the user can use Message<Object> to avoid class cast exceptions.

Perhaps we could have an extra method:

@GenIgnored
<T> MessageConsumer<T> consumer(String address, Class<T> bodyType)

that would use the bodyType to filter the messages before delivery.


-- 
Julien Viet
www.julienviet.com

Matthew Huang

unread,
Oct 23, 2015, 4:21:54 PM10/23/15
to vert.x, matthewh...@gmail.com
Ok, I thought about this again and think that the assumption "the user know the type of objects that will be sent to the consumer address" is true and most people know what they are doing. I also agree that lack of reified generics causes the type parameter not really hold at runtime after the compiler erased that. This is really a well-known JVM issue, not vertex one. I understand the purpose you described. I don't think we need to over-engineer it by adding this extra method (<T> MessageConsumer<T> consumer(String address, Class<T> bodyType)) as chances are, people will use the right type of their input if they start using this extra method.

Thanks again Juliet for the clarification. 

Matthew
Reply all
Reply to author
Forward
0 new messages