3

I have self bounded generic like :

public interface SelfConfigurable<E extends SelfConfigurable<E>> {
    E configure(JsonObject settings);
}

And another interface, also with generic type, which extends my previous interface:

public interface ConfigurableSegmentFilter<T> extends SegmentFilter<T>, SelfConfigurable<ConfigurableSegmentFilter<T>> {
}

I also have an implementation like this

public abstract class ConfigurableSegmentFilterSkeleton<T> implements ConfigurableSegmentFilter<T> {
    @Override
    public ConfigurableSegmentFilter<T> configure(JsonObject settings) {
     ... }
}

I m instantiating the object through reflection and want to configure it before adding to the List:

List<ConfigurableSegmentFilter<?>> result = ...

ConfigurableSegmentFilter newFilter = Reflection.newInstance() + casting

result.add(newFilter.configure(...)); <-- compile error 'cannot be applien to SelfConfigurable' but why?

//when i call to configure directly i am getting:
SelfConfigurable configure = newFilter.configure(...) <-- why SelfConfigurable??

And am i getting a compile error! It told me that xxx.configure() returns SelfConfigurable interface instead of ConfigurableSegmentFilter, i can't uderstand why its happening.

One more thing, when i am bounding the newFilter with wildcard things starting to work as expected

List<ConfigurableSegmentFilter<?>> result = ...

ConfigurableSegmentFilter<?> newFilter = ... //<-- bound with <?>

result.add(newFilter.configure(...)); <-- No error! But why?

The only difference in ConfigurableSegmentFilter vs ConfigurableSegmentFilter<?>

Gelerion
  • 1,634
  • 10
  • 17
  • 1
    The compiler error happens because you're using a raw type. See http://stackoverflow.com/a/2770692/2891664 under the heading *'A raw type is the erasure of that type'*. – Radiodef Jan 26 '17 at 07:18
  • What is `JinniSegmentFilter`???? – Chetan Kinger Jan 26 '17 at 07:18
  • Sorry, i mistakenly added it. I understand that i am using raw type, anyway i can't understand the logic behind it. ConfigurableSegmentFilter> should be the same as ConfigurableSegmentFilter, which equals to ConfigurableSegmentFilter with no bound at all. – Gelerion Jan 26 '17 at 07:46
  • @Gelerion Nope, ConfigurableSegmentFilter> is not the same as ConfigurableSegmentFilter as ? could stand for anything (e.g. String). In Generics Clazz is not assignable from Clazz even if B extends A. This might seem anti-intuitive but there are reasons for this around voiding runtime cast exceptions. – Valentin Ruano Jan 27 '17 at 17:49
  • @Gelerion I have to say I am surprised that you don't get an error in result.add(...). I don't think the compiler would be able to tell that the two '?' are making reference to the same type. Is this really all the relevant code for the problem at hand or are you omitting some? – Valentin Ruano Jan 27 '17 at 17:59
  • @Valentin Ruano but i am not bounding the List> with extends something like List> in this case i can not add the object to the result list, because compiler doesn't know that String is final class and can not be extended. – Gelerion Jan 29 '17 at 11:49
  • @Gelerion it should matter... in theory the compiler should not now what '?' is.... so it should fail because for example if the first '?' is Number and the second '?' is String the .add operation would cause problems down the road... it should fail in compilation time even if in practice you are passing them compatible typed objects in runtime. – Valentin Ruano Jan 30 '17 at 17:42
  • @Gelerion ... i have done some more test and now I see why it works... my bad. I can see that in some scenarios one should be allowed to add mixes of ConfigurableSegmentFilter with different X in the list ... I guess that the following should cause at least a warning: List> x = new ArrayList>(); – Valentin Ruano Jan 30 '17 at 18:13
  • @Gelerion... I checked and indeed it fails to compile with an error. Ok I learned something today... thanks. – Valentin Ruano Jan 30 '17 at 18:15
  • @Valentin Ruano you can also look at (since java 7) NetworkChannel.supportedOptions() it returns Set> where actual implementation of wildcard (?) could be different. Going with your example: In first case, for example, we have List ints and other List extends Integer> extInts here assignment extInts = ints is possible and correct, BUT if we took List> strComp and List> extStrComp here assignment extStrComp = strComp fails with compile error. Very interesting ) – Gelerion Jan 31 '17 at 10:52

2 Answers2

0

It is not true that a 'raw' type is the same a generic where are type parameters are set to 'Object'.

The reason for this is to avoid runtime cast exceptions.

So for example let consider a simpler scenario involving the 'java.util.List' class:

List a = ... # a elements could be a mix of any type.
List<?> b = ... # b elements are all instance of a known class '?'.
List<Object> c = ... # c elements are all instances of Object.
List<String> d = ... # d elements are all instances of String.

It should be clear why operation moving elements from any other list to 'd' must be problematic, as there is no guarantees that those elements are 'String's

However what about any other combos?

  1. a, c or d -> b.

    b.addAll(c); // error!!!
    

    '?' here represent a unknown type... for example it could be String, and in such case we are back to the clear case c -> d. The reason why this restriction is in place is to prevent runtime exceptions down the road when some other piece of code that actually knows the collection's type parameter, say String, and is consuming it elements pulls a non-string instance resulting in a runtime cast exception.

  2. b, a, d -> c or b, c, d -> a.

    Allowed, after all whatever '?' all instance are 'Object' so no problem there. a

Also notice that some reference assigning my be a problem (at least a warning):

   c = a; // unchecked warning; 
   c = d; // error as now you would be able to add non-strings to 'd' thru 'c'.
   c = b; // error, same as above where the the element type is unknown.
   a = d; a = b; // same as with 'c'.
   d = c; d = a; // error for obvious reason. 
Valentin Ruano
  • 2,726
  • 19
  • 29
  • Thank you for the answer and explanation. I do understand that 'raw' type is not the same as wildcard generic. I read the Java Generics and Collections book, anyway i can't understand what's the difference in my specific case. I am not omitting any relevant code, just tried it on a new clean project and newFilter.configure(..) returns me SelfConfigurable when i am using raw object instead of wildcard bounded object. Maybe its because of bridge method? – Gelerion Jan 29 '17 at 11:37
0

When you use an expression of raw type, you "turn off" all the generics in the things you do with that expression, including method signatures and supertype signatures. That means, the raw type ConfigurableSegmentFilter extends the raw type SelfConfigurable only, and its instance method .configure() therefore returns the erasure of E, which is SelfConfigurable.

That's why you should avoid using raw types, as they are only for backwards compatibility. When the type argument is unknown, you can use a wildcard-parameterized type.

newacct
  • 119,665
  • 29
  • 163
  • 224