Qual é a diferença entre <? estende Foo> e <Foo>


20

Parece que tenho um mal-entendido sobre a diferença entre <Foo>e <? extends Foo>. Pelo meu entendimento, se tivéssemos

ArrayList<Foo> foos = new ArrayList<>();

Isso indica que objetos do tipo Foopodem ser adicionados a essa lista de matrizes. Como as subclasses de Footambém são do tipo Foo, elas também podem ser adicionadas sem erro, conforme ilustrado por

ArrayList<Foo> foos = new ArrayList<>();
foos.add(new Foo());
foos.add(new Bar());

onde Bar extends Foo.

Agora, digamos que eu tenha definido fooscomo

ArrayList<? extends Foo> foos = new ArrayList<>();

Meu entendimento atual é que isso se expressa some unknown type that extends Foo. Entendo que isso significa que quaisquer objetos que são uma subclasse de Foopodem ser adicionados a esta lista; significando que não há diferença entre ArrayList<Foo>e ArrayList<? extends Foo>.

Para testar isso, tentei escrever o seguinte código

ArrayList<? extends Foo> subFoos = new ArrayList<>();
subFoos.add(new Foo());
subFoos.add(new Bar());

mas foi solicitado o seguinte erro de compilação

no suitable method found for add(Foo)
method java.util.Collection.add(capture#1 of ? extends Foo) is not applicable
(argument mismatch; Foo cannot be converted to capture#1 of ? extends Foo)

no suitable method found for add(Bar)
method java.util.Collection.add(capture#2 of ? extends Bar) is not applicable
(argument mismatch; Bar cannot be converted to capture#2 of ? extends Bar)

Com base no meu entendimento atual, posso ver por que não consigo adicionar a Fooa uma lista de <? extends Foo>, porque ela não é uma subclasse; mas estou curioso para saber por que não consigo adicionar um Barà lista.

Onde está o buraco no meu entendimento?



1
<? extends Foo>é uma classe específica e desconhecida que se estende Foo. Uma operação com esta classe é legal apenas se for legal para qualquer subclasse de Foo.
Ordous

3
Uau. Quanto mais você aprende sobre os genéricos de Java, mais erros você encontra.
Mason Wheeler

Respostas:


15

Como você descobriu por si mesmo, um ArrayListdeclarado como ArrayList<? extends Foo> subFoos = new ArrayList<>();não seria muito útil.

Para ver a utilidade de <? extends T>considere isso:

List<Foo> collect( List<? extends Foo> a1, List<? extends Foo> a2 )
{
    List<Foo> collected = new ArrayList<>();
    collected.addAll( a1 );
    collected.addAll( a2 );
    return collected;
}

que posteriormente pode ser usado da seguinte maneira:

List<Foo> foos = collect( new ArrayList<Foo>(), new ArrayList<Bar>() );

ou da seguinte forma:

List<Foo> foos = collect( new ArrayList<Bar>(), new ArrayList<Foo>() );

observe que nenhuma das opções acima teria funcionado se o collectmétodo tivesse sido declarado da seguinte maneira:

List<Foo> collect( List<Foo> a1, List<Foo> a2 )
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.