5
respostas

<? super String> : diferenças quando usado com uma lista ou com um Predicate

Caros,

Dado o seguinte código,

List<? super String> listaString = new ArrayList<String>();

não é possível fazer algo do tipo

listaString.stream().map(s -> s.toUpperCase()).forEach(System.out::println);

pois o <? super String> indica que podemos referenciar uma lista de qualquer tipo que seja superclasse de String, e não faz sentido chamar o método toUpperCase() caso tenhamos uma List< Object >, por exemplo. Sendo assim, por que o código a seguir compila?

Predicate<? super String> predicadoString = str -> str.length() > 2;

Da mesma forma que na declaração da lista, o wildcard <? super String> indica um supertipo de String. Não temos garantia de que este tipo possui um método length(). Por que este segundo exemplo compila?

E para completar, por que a seguinte atribuição é válida

Predicate<Object> predicadoObjeto = obj -> true;
predicadoString = predicadoObjeto;

porém a chamada a seguir resulta em erro:

System.out.println(predicadoString.test(new Object()));
5 respostas

Como vc está utilizando "(System.out::println)" que imprime o "toString" , conseguir compilar com a seguinte modificação :

listaString.stream().map(s -> s.toString().toUpperCase()).forEach(System.out::println);

Não vejo nada de errado , apenas a inferência de tipos , não conseguiu determinar o tipo e reclamou com um erro de compilação.

E só fazendo alguns testes , a primeira opção compilou , a segunda não...

listaString.stream().map(s -> (((String) s).toUpperCase()) ).forEach(System.out::println); listaString.stream().map((String s) -> s.toUpperCase()).forEach(System.out::println);

Sim, na verdade o que me deixou confuso foi esta declaração compilar:

Predicate<? super String> predicadoString = str -> str.length() > 2;

A declaração

Predicate<? super String>

pode se referir tanto a um Predicate<String> quanto um Predicate<Object>, então como ele permite que seja usado o método length() no argumento do método abstrato test()? (já que esse argumento poderia ser tanto um Object quando uma String)

O argumento é pelo menos uma String, porquê você esta limitando inferiormente com "super".

Com isso eu vejo, nenhum problema aqui:

Predicate<? super String> predicadoString = str -> str.length() > 2;

https://docs.oracle.com/javase/tutorial/java/generics/bounded.html

Exato, é pelo menos uma String, mas poderia ser um Object, e neste caso não faria sentido chamar o método length. Veja que estou usando super e não extends.

Um outro exemplo para deixar o problema mais explícito:

class A{ public A a() {return new A();};};
class B extends A{ public B b() {return new B();};};
class C extends B{ public C c() {return new C();};};
class D extends C{ public D d() {return new D();};};


UnaryOperator<? super C> p1 = arg -> arg.c();
UnaryOperator<? super C> p2 = arg -> arg.a();
UnaryOperator<? super C> p3 = arg -> arg.d();

Desses 3 operadores, apenas p1 compila.

p2 dá o seguinte erro: Type mismatch: cannot convert from A to C

p3 dá o seguinte erro: The method d() is undefined for the type C

O erro em p3 é até esperado. Mas como explicar o erro de compilação em p2?