0

Given this code:

public final class MyMap extends HashMap<MyClass<? extends MyInterface>, MyInterface>
{
    public <T extends MyInterface> T put(MyClass<T> key, T value)
    {
        return (T)key.getClass().getComponentType().cast(super.put(key, value));
    }
}

I get an unchecked cast warning on the cast from MyInterface to T.

I really dont like it so I use the Class.cast() method to cast it instead. Now my new function looks like this:

public final class MyMap extends HashMap<MyClass<? extends MyInterface>, MyInterface>
{
    public <T extends MyInterface> T put(MyClass<T> key, T value)
    {
        Class<T> cl = (Class<T>)key.getClass().getComponentType();
        return cl.cast(super.put(key, value));
    }
}

But this one gives an unchecked cast warning from Class<?> to Class<T> as Object.getClass() returns a wildcard class.

However, is there any chance that key.getClass().getComponentType() will not return Class<T> as T is defined by the component type of the class of key?

Wietlol
  • 1,001
  • 1
  • 10
  • 25
  • 5
    Are you sure you want to be calling `getComponentType` there? `getComponentType` returns e.g. `String.class` if you call `new String[] {}.getClass().getComponentType()`. http://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#getComponentType-- – Radiodef Apr 21 '17 at 20:11
  • 3
    Why are you calling getComponentType? `key` does not seem to be an array... –  Apr 21 '17 at 20:13
  • 2
    The good news is that the cast is safe in practice, in the sense that it will never cause a `ClassCastException`. The bad news is that that arises from the fact that that invocation of `getComponentType()` will always return `null`, because the `Class` on which it is invoked does not (cannot) represent an array. – John Bollinger Apr 21 '17 at 20:19
  • I copied it from a function that created arrays based on generic types... I thought it wouldnt do much harm, but what function should I use instead of getComponentType()? – Wietlol Apr 21 '17 at 20:20
  • 2
    You'd need to manually define a method in `MyClass` that returns it's components type, there is no automatic way. The default behaviour is [type erasure](https://docs.oracle.com/javase/tutorial/java/generics/erasure.html). – Jorn Vernee Apr 21 '17 at 20:21
  • I think ill stick to the unchecked cast then. Passing the class of T around everywhere will hurt the user friendlyness of my library. And I dont see any real harm really as all the input has to be done via that put method... which ensures that MyClass keys have T as values. Thanks for the answers though. – Wietlol Apr 21 '17 at 20:31
  • Well, watch out for invocations of `putAll()`, `merge()`, `compute()`, `replace()`, and related methods, then, as well as `setValue()` operations on elements of the map's entry set. Since you're inheriting from `HashMap`, you get all of these possibilities, and even if some of them operate by invoking your override of `put()`, their signatures do not enforce the relationship between keys and values that you want to rely upon. – John Bollinger Apr 21 '17 at 20:42
  • 2
    The basic problem here is that you're trying to rely on a type relationship that is enforced only functionally, as opposed to being inherent in any of the types involved. – John Bollinger Apr 21 '17 at 20:46
  • 1
    Other SO questions have addressed this before. There is no way for Java generics to enforce a Map where each key maps to a value whose generic type corresponds to the key type. Attempting to do so will always entail fighting generics. – VGR Apr 21 '17 at 22:01

0 Answers0