IsKernel
IsKernel

Reputation: 333

Google Guice binding issue

I'm using the Google Guice IoC container. I have two packages, one containing interfaces and one containing implementations for those interfaces.

The ClassFinder class returns a list of classes from a package.

When I try to bind the interface to an implementation, I receive the following compile error: Cannot resolve method to <java.lang.Class<capture<?>>from the to method.

The API specifies that to can take as a parameter Class and packageClass is Class. What should be the problem?

public void autoMatch(String basePackageName)
    {
        String interfacesPackage = basePackageName + "." + interfacesPackageName;
        String implementationPackage = basePackageName + "." + implementationPackageName;

        List<Class<?>> interfaces = ClassFinder.find(interfacesPackage);
        List<Class<?>> implementations = ClassFinder.find(implementationPackage);
        for(Class<?> packageClass : implementations)
        {
            String name = packageClass.getSimpleName();
            try
            {
               Class<?> foundInterface
                         = interfaces.stream()
                          .filter(packageInterface -> packageInterface.getSimpleName().equals(name + "Interface"))
                          .findFirst().get();
                bind(foundInterface).to(packageClass);
            }
            catch (NoSuchElementException exception)
            {
                Log.error("IoC", "Could not match interface to implementation", exception);
            }
        }
    }

EDIT

Managed to solve the issue by casting to (Class). The class is found, but it throws a java.lang.NullPointerException when binding.

Upvotes: 0

Views: 1335

Answers (2)

The problem is that you loose the information at compile-time the class implements the interface. From the compilers point of view you end up with two unrelated Class<?> objects and as Guice is written with generics there is no method for binding them to each other.

Untested: Consider using Class instead of Class<?> so generics are not used.

That said, I would suggest you write the 20-30 bindings out by hand. The idea with Dependency Injection is to decouple interfaces from their implementation and your approach will introduce a subtle, hard to debug runtime dependency between them which I do not think is a good thing.

Upvotes: 1

IsKernel
IsKernel

Reputation: 333

Fixed the issue by no longer using ClassFinder to find the classes and by using the Reflections library.

public void autoMatch(String basePackageName)
{
    String interfacesPackage = basePackageName + "." + interfacesPackageName;
    String implementationPackage = basePackageName + "." + implementationPackageName;

    Reflections interfacesReflections = new Reflections(interfacesPackage);
    Reflections implementationsReflections = new Reflections(implementationPackage);

    Set<Class<? extends Object>> interfaces = interfacesReflections.getSubTypesOf(Object.class);
    Set<Class<? extends Object>> implementations = implementationsReflections.getSubTypesOf(Object.class);

    for(Class<?> packageClass : implementations)
    {
        String name = packageClass.getSimpleName();
        try
        {
            Class<?> foundInterface
                    = interfaces.stream()
                      .filter(packageInterface -> packageInterface.getSimpleName().equals(name + "Interface"))
                      .findFirst().get();
            bind(foundInterface).to((Class)packageClass);
        }
        catch (NoSuchElementException exception)
        {
            Log.error("IoC", "Could not match interface to implementation", exception);
        }
    }
}

Upvotes: 1

Related Questions