Generic: ArrayList of ? Extends ISomeInterface in

2019-07-04 02:47发布

问题:

I'm having some trouble in the following code.

public ArrayList<? extends IEvent> getEventsByDateRange(DateTime minStartTime,  DateTime minEndTime) 
{
    ArrayList<? extends IEvent> returnedEvents = new ArrayList<GoogleEvent>();
    returnedEvents.add(new GoogleEvent());
    return (returnedEvents);
}

This return the following compilation error for the "returnedEvents.add(new GoogleEvent()); line of code":

The method add(capture#1-of ? extends IEvent) in the type ArrayList is not applicable for the arguments (GoogleEvent)

The declaration of the class GoogleEvent is as follows:

public class GoogleEvent implements IEvent {...}

I know there are some tricky parts using generics in Java, thus the wild-cards but I can't seem to figure this out.

Thanks.

回答1:

Why don't you write:

public List<IEvent> getEventsByDateRange(DateTime minStartTime, DateTime minEndTime)
{
    List<IEvent> returnedEvents = new ArrayList<IEvent>();
    returnedEvents.add(new GoogleEvent());
    return returnedEvents;
}


回答2:

You don't need to use ? extends IEvent because by using only IEvent, Java will dynamically bind the GoogleEvent class. It's polymorphism.



回答3:

The solution:

ArrayList<IEvent> returnedEvents = new ArrayList<IEvent>();

The reason of your error is because the compiler is trying to do capture conversion.

In your case returnedEvents captures some unknown that extends IEvent (i.e. anything that extends/implements IEvent) and you're assigning it to a parameterized type GoogleEvent).

The compiler sees, returnedEvents.add(? extends IEvent) which doesn't validate with a signature of returnedEvents.add(GoogleEvent) as the capture placeholder is set on returnedEvents.



回答4:

This is not allowed. returnedEvents contains a list of GoogleEvent objects and must not be allowed to accept objects that are not GoogleEvent objects.

Although in your example you happen to be passing a GoogleEvent, you are doing so by calling a version of add that accepts anything that implements IEvent. That add method simply isn't allowed to be called, because it could result in the list storing things other than GoogleEvent.

Java wildcards "edit out" methods that would break the rules in this way.

If you need to return that wildcarded list type, an ArrayList<GoogleEvent> satisfies it fine.

Note this is a complete source file that compiles without error:

import java.util.*;

interface IEvent { }

class GoogleEvent implements IEvent { }

public class Foo {
    public ArrayList<? extends IEvent> getEventsByDateRange()  {
        ArrayList<GoogleEvent> returnedEvents = new ArrayList<GoogleEvent>();
        returnedEvents.add(new GoogleEvent());
        return (returnedEvents);
    }
}


回答5:

public ArrayList<IEvent> getEventsByDateRange(DateTime minStartTime,
        DateTime minEndTime)
{
    ArrayList<IEvent> returnedEvents = new ArrayList<IEvent>();
    returnedEvents.add(new GoogleEvent());
    return (returnedEvents);
}


回答6:

You cannot writer to a variable which is declared with ? extends .... Writing is only allowed when using ? super ....

This can be explained by the following example:

List<GoogleEvent> googleEvents = new ArrayList<GoogleEvent>();
ArrayList<? extends IEvent> returnedEvents = googleEvents;

// Would be OK
returnedEvents.add(new GooleEvent());

// Would be OK to the definition of returnedEvents, 
// but breaks the googleEvents.
returnedEvents.add(new OtherEvent());

The short solution for you is to declare returnedEvents as List<GoogleEvent>.