简体   繁体   中英

Java Self bound generic

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<?>

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. 

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM