2

I have following interfaces:

CacheKey interface:

public interface CacheKey<K extends Serializable> extends Serializable {
    K get();
}

CacheValue interface:

public interface CacheValue<V extends Serializable> extends Serializable {
    V get();
}

Cache interface:

public interface Cache<CacheKey<K extends Serializable>, CacheValue<V extends Serializable>> {
}

Cache interface doesn't compile. Error I get is: [13,35] > expected

i.e. after CacheKey compiler doesn't like another opening angle bracket:

public interface Cache<CacheKey<
                               ^

Is this not possible in Java?

2 Answers 2

1

You're missing a crucial step here : the implementation.

It's usually in the implementation that you want to define what the type of ? extends Serializable will be. You don't need to implement this in the Cache interface.

The interface only needs to know what its generics types will be, not the generics of their child : this is for the implementation.

Take a look at the example here below to understand what I exactly mean by that.

Addition : When your define something like Cache<CacheKey, CacheValue> you're not referring to the classes, but you're creation a generic alias. CacheKey could easily be replaced by Blabla and continue to have the same behaviour. The solution is to use extends to make sure we're talking about the type.

This was also the reason why Cache<CacheKey<...>> did not compile, because CacheKey is not referring to the class but is used as an alias

public interface CacheKey<K extends Serializable> extends Serializable {
    K get();
}

public interface CacheValue<V extends Serializable> extends Serializable {
    V get();
}

public interface Cache<K extends CacheKey<? extends Serializable>, V extends CacheValue<? extends Serializable>> {
    void put(K key, V value);
}

public class CacheImpl implements Cache<CacheKey<String>, CacheValue<String>> {
    @Override
    public void put(CacheKey<String> key, CacheValue<String> value) {
        // do whatever
    }
}
Sign up to request clarification or add additional context in comments.

5 Comments

K extends CacheKey and V extends CacheValue will generate compiler warnings due to the use of raw types, which negates the entire benefit of using generics.
@VGR Are you sure about the warnings? I'm not sure about losing the benefits of working with generics, since the implementation enforces them..
@VGR It does not issue any warning, but I modified the code nonetheless, for clarity and also by explicitly putting the generic type, you enforce the implementation to not work with raw types. But no warning issued.
Try compiling with all warnings enabled. On the command line, that is done with -Xlint. When I do it, I get Cache.java:12: warning: [rawtypes] found raw type: CacheKey.
Thanks YassinHajaj - Your subsequent update led me to right solution.
0

Using a wildcard-bounded generic type, as suggested in other answers, isn't a great idea.

If you declare your class like this:

public interface Cache<K extends CacheKey<?>, V extends CacheValue<?>> {
}

then you would never be able to usefully invoke get() on the key or value, because they would only ever return Serializable. And Serializable is a useless class (it's barely different to Object actually inside your code).

Instead, I would simply declare the class like:

public interface Cache<K extends Serializable, V extends Serializable> {

and then declare that the put method takes a CacheKey<K> and a CacheValue<V>:

  void put(CacheKey<K> key, CacheValue<V> value);

because, ultimately, all implementations of CacheKey<K> and CacheValue<V> should be indistinguishable.

If you really want to force the CacheKey<K> and CacheValue<V> to be of specific types, you need to add more type variables:

public interface Cache<
    K extends Serializable, CK extends CacheKey<K>,
    V extends Serializable, CV extends CacheValue<V>> {
  void put(CK key, CV value);
}

but this is really quite gross, as you would have to carry around all of these 4 type variables wherever you use a Cache type directly.

Of course, you can specialize the interface to hide some of these type variables:

interface StringKey extends CacheKey<String> {}
interface IntegerValue extends CacheValue<Integer> {}
interface StringIntegerCache extends Cache<String, StringKey, Integer, IntegerValue> {}

and then just use StringIntegerCache. The usefulness of doing so depends on your application.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.