Hey guys, I have a problem with generic types and can't figure out why. Hope you can help me. StatusModifier<T>#getValue() returns a T and I'd like to implement a method that can get a StatusModifier from an object without the need to cast. This is what I have: Code (Text): default <V> StatusModifier<V> getStatusModifier(Class<V> type, String key) { for (StatusModifier<?> status : getStatusModifiers()) { if (!type.isInstance(status.getValue())) { continue; } if (status.getKey().equals(key)) { return (StatusModifier<V>) status; } } return null; } However calling this gives me an unchecked warning: Code (Text): StatusModifier<Double> test = gui.<Double>getStatusModifier(Double.class, ~); The IDE says "required: StatusModifier<Double>" and consequently, Code (Text): Double test = gui.<Double>getStatusModifier(Double.class, ~).getValue(); still needs a cast which makes all of this kind of pointless. Do you know a fix? ~Best regards Sataniel
Code (Text): Double test = gui.getStatusModifier(Double.class, "MasterKey").getValue() This should do the generic thing. Could you post your StatusModifier class if the above snippet doesn't work.
Yes, I also expected this to work. https://github.com/DRE2N/Vignette/b...thon/vignette/api/context/StatusModifier.java https://github.com/DRE2N/Vignette/b...thon/vignette/api/context/Contextualized.java These are the classes with the only difference that Contextualized has the #getStatusModifier(Class<V>, String) method I posted in the OP.
Is this the latest version? https://github.com/DRE2N/Vignette/b...ette/api/context/Contextualized.java#L95-L109 It is different compared to your first posted snippet.
No. Both are in the class I'm using. Edit: By the way, I'm using this method to get an enum value: Code (Text): public static <E extends Enum<E>> E getEnum(Class<E> enumClass, String valueName) { if (enumClass == null || valueName == null) { return null; } try { return Enum.valueOf(enumClass, valueName); } catch (IllegalArgumentException exception) { return null; } } And Code (Text): Material test = getEnum(Material.class, Material.AIR.name()); works fine without warnings.
Sure, although it won't say much more. https://github.com/DRE2N/Vignette/b...thon/vignette/api/context/Contextualized.java https://github.com/DRE2N/Vignette/commit/54ae66286e78cdad11e7cc26be2fd633343671fc
I think there is no solution without migrating to a Map<String, StatusModifier<?>>. Second thing I recommend is moving most of the implementation out of the interface. Sorry
The reason why this doesn't work is because Java is unable to figure out on its own that, when you filter out every StatusModifier that is not a StatusModifier<V>, you will be left with only StatusModifier<V>s. Since a cast from StatusModifier<?> to StatusModifier<V> might be unsafe, you're left with this warning. As for why your enum example does not give such issues, it's quite simple: at no point do you do such a cast. You make a call to Enum.valueOf, which in turn gives you your enum value by the given name. The Enum class itself doesn't have such warnings either, since it internally retrieves values from a Map<String, T>, where T is the same as your Class<T>. So, from Class<T> in your method to Map<String, T> in Java's Class (same T) to a single T from that Map (same T), back to your method as return value (same T). At no point was any conversion necessary. From a larger perspective, no conversion needs to happen, since the Class only has one type of data to store: T. This makes sense, since if you have an enum - let's call it MyEnum - all constants declared in there, will be of type MyEnum. You can't have a constant in MyEnum, that is suddenly of YourEnum. Therefore, the Map only needs to store values of T (in this case T would be MyEnum). This differs from your example, where your internal storage can store multiple types of StatusModifier, which means that they will be stored as StatusModifier<?>. This means that you could have a StatusModifier<String> and a StatusModifier<Integer>, but we don't know that, since any information we may have had about this generic is lost.
Thank you anyway for your time! However I believe having the convenience function implemented as default methods in the interface is fine. The JDK also uses default methods fairly much in its interfaces, like e.g. Collection#removeIf(), #stream(), #iterator(), List#replaceAll().