简体   繁体   中英

Java Type Wildcarding

(For the purposes of this post, lets set aside java.util.Observable)

I was experimenting around with generics, and then wildcard types. The aim was to create a type-generic observable cache with deltas provided to the observers. Where this starts to go off the rails is I wanted to allow more generic observers to be used than the one specified in the Observable, eg Observer<Object> or some other common superclass.

I've since concluded that this is overly complex for my use case, but the problem itself continues to bother me since I clearly don't understand how to use type wildcarding properly.

So if we start with a simple observer interface:

public interface Observer<T> {
    public void notifyChange(ChangeHolder<T> change);
}

And the associated ChangeHolder, in a full implementation this would be more complex, providing lists of added / updated / deleted objects, but this is sufficient to demonstrate the issue

public interface ChangeHolder<T> {
    T getChange();
}

So with the Observer defined, I tried to implement the Observable abstract class:

public abstract class Observable<T> {
    private Set<Observer<? super T>> observers = new HashSet<>();

    public void addObserver(Observer<? super T> obs){
        observers.add(obs);
    }

    public void change(ChangeHolder<T> changes){
        for(Observer<? super T> obs : observers){
            obs.notifyChange(changes);
        }
    }
}

And with that I could define some object caches, by declaring something like class TreeCache extends ObservableCache<Tree> , (From this point on I'll use Tree as an example class to be used as a T, assume it to be a simple POJO extending only from Object) and pass ChangeHolder<Tree> objects to TreeCache.change() when necessary. Unfortunately the compiler disagrees:

The method notifyChange(ChangeHolder<capture#2-of ? super T>) in the type Observer<capture#2-of ? super T> is not applicable for the arguments (ChangeHolder<T>)

Which is where my understanding ends.

Without the ChangeHolder class (if my notifyChange method just took a plain T instead) it works just fine since it's perfectly legal to pass a Tree to Observer.notifyChange(Object).

I inferred that I should be able to do the same with the ChangeHolder - ChangeHolder<T> should satisfy notifyChange(ChangeHolder<? super T>) in the same way that T satisfies notifyChange(? super T) but clearly I am misunderstanding something?

There is no wildcard in the signature notifyChange(ChangeHolder<T> change) . Therefore the generic type of the passed argument must exactly match the generic type of the Observer instance.

Observer<? super T> Observer<? super T> means an Observer of some unknown type that is a supertype of T . Since the generic type of obs may not exactly match the generic type of changes , the notifyChange method is not applicable.

There are two possible fixes:

  1. Change the signature to notifyChange(ChangeHolder<? extends T> change) so that the method works for subtypes.
  2. Get rid of the wildcards everywhere, so that you have just <T> instead.

I prefer solution 1, as it is a good idea for signatures to be as general as possible.

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