Binding a wildcard generic and a typed generic to the same instance : is black magic possible?

81 views
Skip to first unread message

elect...@gmail.com

unread,
Jan 6, 2016, 3:19:34 PM1/6/16
to google-guice

I'm building some kind of framework and the developers have to specify the type of server they want to use. The IServer<?> is the interface of the server : it is parametized.

The framework itself only needs the wildcard version of the IServer<?> instance to be injected in its classes. In fact, it doesn't even know the type of the Server that will actually be used. So :

bind(new TypeLiteral<IServer<?>>(){}).to(getServerClass()).in(Scopes.SINGLETON);

The dev would define a getServerClass() method like :

protected Class<? extends IServer<?>> getServerClass() {
    
return SomeServer.class; // SomeServer implements IServer<TheTypeTheDevWants>
}

But the dev will also need a typed version, IServer<TheTypeTheDevWants>, to be injected in his classes. So :

bind(new TypeLiteral<IServer<TheTypeTheDevWants>>(){}).to(getServerClass()).in(Scopes.SINGLETON);

First question : does this actual create two instances or only one? Two I guess and this is my first problem. How could I bind the same instance to both types?

Second question :

Would it be possible to ask the dev to define a getServerType() method, that would return "TheTypeTheDevWants", in a way or another, so the framework can use it to create the two bindings by itself? Something like :


bind
(new TypeLiteral<IServer<?>>(){}).to(getServerClass()).in(Scopes.SINGLETON);
bind
(new TypeLiteral<IServer<getServerType()>>(){}).to(getServerClass()).in(Scopes.SINGLETON); // Black magic required here!


Is there any way for the framework to bind IServer<TheTypeTheDevWants> dynamically, without knowing "TheTypeTheDevWants" at compile time?

I try to make the life easier for the dev. I'd like the framework to do both binding by itself here. Is it possible?

I hope my questions makes sense! :-)

Tavian Barnes

unread,
Jan 7, 2016, 1:46:04 PM1/7/16
to google-guice
On Wednesday, 6 January 2016 15:19:34 UTC-5, elect...@gmail.com wrote:

First question : does this actual create two instances or only one? Two I guess and this is my first problem. How could I bind the same instance to both types?

Yes, as written you'll have two separate singletons.  You want to bind server implementation *itself* as a singleton, like this:

bind(getServerClass()).in(Scopes.SINGLETON);

Second question :

Would it be possible to ask the dev to define a getServerType() method, that would return "TheTypeTheDevWants", in a way or another, so the framework can use it to create the two bindings by itself? Something like :
 
Yep, but it's not pretty :)

Type type = Types.newParameterizedType(IServer.class, getServerType());
TypeLiteral<?> typeLiteral = TypeLiteral.get(type);
bind
(typeLiteral).to(getServerType());

There are probably some casts missing from that snippet, you may have to use raw types to get it to compile, but that's the gist.

elect...@gmail.com

unread,
Jan 7, 2016, 7:47:26 PM1/7/16
to google-guice

Tavian, thanks for the help!



Yes, as written you'll have two separate singletons.  You want to bind server implementation *itself* as a singleton, like this:

bind(getServerClass()).in(Scopes.SINGLETON);



 That seems to work great, thanks!



Type type = Types.newParameterizedType(IServer.class, getServerType());
TypeLiteral<?> typeLiteral = TypeLiteral.get(type);
bind(typeLiteral).to(getServerType());

There are probably some casts missing from that snippet, you may have to use raw types to get it to compile, but that's the gist.


 I'm unable to make this work!


And, the IDE help for the parameters :




Any idea?

elect...@gmail.com

unread,
Jan 7, 2016, 8:09:06 PM1/7/16
to google-guice
I got it actually! :-)


Of course the last binding has to be done on "getServerClass()" not on "getServerType()", but othewise your snippets works well! Thanks a lot!!

Now, let's say I want to be really, really fancy, and extract the parametrized type "HttpServerExchange" from the getServerClass() return... So the dev wouldn't even have to define "getServerType()"! Do you think it's possible? 

I'll try!

Tavian Barnes

unread,
Jan 7, 2016, 9:12:58 PM1/7/16
to google-guice
On Thursday, 7 January 2016 20:09:06 UTC-5, elect...@gmail.com wrote:
Now, let's say I want to be really, really fancy, and extract the parametrized type "HttpServerExchange" from the getServerClass() return... So the dev wouldn't even have to define "getServerType()"! Do you think it's possible?

TypeLiteral<?> iserverType = TypeLiteral.get(getServerClass()).getSupertype(IServer.class);
Type serverType = ((ParameterizedType) iserverType.getType()).getActualTypeArguments()[0];



elect...@gmail.com

unread,
Jan 8, 2016, 10:33:32 AM1/8/16
to google-guice
Wow, that actually works!

My final code, if it can help someone, one day :

@SuppressWarnings({ "unchecked", "rawtypes" })
protected void bindServer() {
   
    bind
(getServerClass()).in(Scopes.SINGLETON);

    bind
(new TypeLiteral<IServer<?>>(){}).to(getServerClass()).in(Scopes.SINGLETON);
   

   
TypeLiteral<?> serverType = TypeLiteral.get(getServerClass()).getSupertype(IServer.class);
   
Type exchangeType = ((ParameterizedType) serverType.getType()).getActualTypeArguments()[0];
   
Type parameterizedType = Types.newParameterizedType(IServer.class, exchangeType);
   
TypeLiteral<?> typeLiteral = TypeLiteral.get(parameterizedType);
    bind
(typeLiteral).to((Class)getServerClass());
}

protected Class<? extends IServer<?>> getServerClass() {

   
return UndertowServer.class;
}



Thanks a lot for the help Tavian!

elect...@gmail.com

unread,
Jan 8, 2016, 10:37:09 AM1/8/16
to google-guice
In my case, the last binding should be a singleton too, though :

//...
bind
(typeLiteral).to((Class)getServerClass()).in(Scopes.SINGLETON);


 

elect...@gmail.com

unread,
Jan 8, 2016, 12:26:57 PM1/8/16
to google-guice
Well, I should have RTFM indeed : I see that the "in(Scopes.SINGLETON);" are in fact not required once the implementation class is bound.

So the final code would be :

@SuppressWarnings({ "unchecked", "rawtypes" })
protected void bindServer() {
   
    bind
(getServerClass()).in(Scopes.SINGLETON);

    bind
(new TypeLiteral<IServer<?>>(){}).to(getServerClass());
Reply all
Reply to author
Forward
0 new messages