sector11 sector11 - 1 month ago 9
Java Question

SOLVED: Dagger2 Named(@Named) Injection with polymorphism or different object

Hi i am trying to study named injection in Dagger2

Here are my Java classes but none of them seems to be working.
What i want is that based on @Named annotation i wish to get different objects.

public interface Server {

public void start();

public void stop();

public String request(String request);
}

public abstract class AbstractServer implements Server {

private boolean started;

@Override
public void start() {
started = true;
}

@Override
public void stop() {
if (!started) {
throw new IllegalStateException("Server was not started");
}
}
}

public class AudioServer extends AbstractServer{

@Override
public String request(String request) {
return "Response from Audio server: " + request;
}

}

public class VideoServer extends AbstractServer {

@Override
public String request(String request) {
return "Response from Video server: " + request;
}
}

@Module
public class ServerModule {

public ServerModule() {
}

@Provides
@Named("audio")
@Singleton
AudioServer provideAudioServer() {
return new AudioServer();
}

@Provides
@Named("video")
@Singleton
VideoServer provideVideoServer() {
return new VideoServer();
}
}


Please not ServerComponent.java is not compiling

@Singleton
@Component(modules = {ServerModule.class})
public interface ServerComponent {

AudioServer provideAudioServer();

VideoServer provideVideoServer();

void inject(TestInject inject);

}

public class TestInject {

private static final Logger logger = Logger.getLogger(TestInject.class.getSimpleName());

@Inject
@Named("audio")
Server audioServer;

public TestInject() {
// ServerComponent component = DaggerServerComponent.builder()
// .build();
// component.inject(this);
}

public void test() {
String serverResponse = null;
if (audioServer != null) {
serverResponse = audioServer.request("game.mp3");
logger.warning(serverResponse);
} else {
serverResponse = "Failure";
logger.info(serverResponse);
}
}

public static void main(String[] args) {
TestInject inject = new TestInject();
inject.test();
}
}





EDITED Please see answer in
TestInject.java
and
ServerComponent.java


public interface Server {

public void start();

public void stop();

public String request(String request);
}

public abstract class AbstractServer implements Server {

private boolean started;

@Override
public void start() {
started = true;
}

@Override
public void stop() {
if (!started) {
throw new IllegalStateException("Server was not started");
}
}
}

public class AudioServer extends AbstractServer{

@Override
public String request(String request) {
return "Response from Audio server: " + request;
}

}

public class VideoServer extends AbstractServer {

@Override
public String request(String request) {
return "Response from Video server: " + request;
}
}

@Module
public class ServerModule {

public ServerModule() {
}

@Provides
@Named("audio")
@Singleton
Server provideAudioServer() {
return new AudioServer();
}

@Provides
@Named("video")
@Singleton
Server provideVideoServer() {
return new VideoServer();
}
}


Please not ServerComponent.java is not compiling

@Singleton
@Component(modules = {ServerModule.class})
public interface ServerComponent {

@Named("audio")
Server provideAudioServer();

@Named("video")
Server provideVideoServer();

void inject(TestInject inject);
}

public class TestInject {

private static final Logger logger = Logger.getLogger(TestInject.class.getSimpleName());

@Inject
@Named("audio")
Server audioServer;

@Inject
@Named("video")
Server videoServer;

public TestInject() {
ServerComponent component = DaggerServerComponent.builder()
.build();
component.inject(this);
}

public void testAudioServer() {
String serverResponse = null;
if (audioServer != null) {
serverResponse = audioServer.request("game.mp3");
logger.warning(serverResponse);
} else {
serverResponse = "audio server Failure";
logger.info(serverResponse);
}
}

public void testVideoServer() {
String serverResponse = null;
if (videoServer != null) {
serverResponse = videoServer.request("movie.mp4");
logger.warning(serverResponse);
} else {
serverResponse = "Video server Failure";
logger.info(serverResponse);
}
}

public static void main(String[] args) {
TestInject inject = new TestInject();
inject.testAudioServer();
inject.testVideoServer();
}
}

Answer

Your main problem seems to be related to the fact that you expect in the class TestInject a Server named audio while your provider returns AudioServer so dagger cannot satisfy your dependency.

Indeed don't forget that the annotation @Named is used to distinguish 2 objects of the same type, in other words you can annotate with @Named("audio") different providers as long as they don't return the same type. The object produced will then be identified by its type and its name.

So for example here is one way to fix you problem:

The class TestInject:

public class TestInject {
    ...

    public TestInject() {
        // Needed to inject your dependencies
        ServerComponent component = DaggerServerComponent.builder()
            .build();
        component.inject(this);
    }
    ...
}

The class ServerComponent

@Singleton
@Component(modules = ServerModule.class)
public interface ServerComponent {
    void inject(TestInject inject);
}

The class ServerModule

@Module
public class ServerModule {

    @Provides
    @Named("audio")
    @Singleton
    public Server provideAudioServer() {
        return new AudioServer();
    }

    @Provides
    @Named("video")
    @Singleton
    public Server provideVideoServer() {
        return new VideoServer();
    }
}

Even with your question update, your module should be what I propose abose otherwise it won't compile for the same reason described previously.