1

I am developing a JAX-RS based Java application using Google Guice for dependency injection. I have the following interface in my code:

public interface LockProvider<L extends Lock> {
    Optional<L> acquireLock(String lockId);

    void releaseLock(L lock);
}

In the above interface, Lock is an interface defined as follows:

public interface Lock {
    String getLockId();
}

Lock interface is implemented by the following class:

public class DynamoDBLock implements Lock {
    private final String lockId;
    private final LockItem underLyingLock;

    public DynamoDBLock(final String lockId, final LockItem underLyingLock) {
    this.lockId = lockId;
    this.underLyingLock = underLyingLock;
    }

    @Override
    public String getLockId() {
        return lockId;
    }

    public LockItem getUnderlyingLock() {
        return underlyingLock;
    }
}

LockProvider interface is implemented by the following class:

public class DynamoDBLockProvider implements LockProvider<DynamoDBLock> {
    Optional<DynamoDBLock> acquireLock(String lockId) {
        //implementation here
    }

    void releaseLock(DynamoDBLock lock) {
        LockItem underlyingLockItem = lock.getUnderlyingLockItem();
        //do something with underlyingLockItem
    }
}

I don't want classes in my application other than LockProvider to know about underLying lock item, which is why I haven't included getUnderlyingLockItem in the Lock interface.

Now, when I try to bind LockProvider to DynamoDBLockProvider as follows:

bind(new TypeLiteral<LockProvider<Lock>>() {}).to(DynamoDBLockProvider.class);

I get the following compilation error in Eclipse:

The method to(Class<? extends LockProvider<Lock>>) in the type LinkedBindingBuilder<LockProvider<Lock>> is not applicable for the arguments (Class<DynamoDBLockProvider>)

I understand that DynamoDBLockProvider<DynamoDBLock> is not a subclass of LockProvider<Lock>. Is it possible to accomplish what I am trying to do, i.e. bind LockProvider to DynamoDBLockProvider (in a clean and efficient way)?

jay electricity
  • 299
  • 5
  • 15

1 Answers1

2

Your instances of DynamoDBLockProvider are not instances of LockProvider<Lock>. But they are instances of LockProvider<? extends Lock>.

A parallel example would be instances of ArrayList<Integer> are not instances of List<Number> but are instances of List<? extends Number> as per this question.

Apply that to Guice and you will get exactly the same compilation fail. Assume you have a class like this:

public class IntegerList extends ArrayList<Integer> {}

Then binding it like this won't work:

binder.bind(new TypeLiteral<List<Number>>() {}).to(IntegerList.class); //won't compile

Is it possible to accomplish what I am trying to do, i.e. bind LockProvider to DynamoDBLockProvider (in a clean and efficient way)?

The following binding will work:

binder.bind(new TypeLiteral<LockProvider<? extends Lock>>() {}).to(DynamoDBLockProvider.class);
David Rawson
  • 20,912
  • 7
  • 88
  • 124