El método Collections.fill tiene el siguiente encabezado:
public static <T> void fill(List<? super T> list, T obj)¿Por qué es necesario el comodín? El siguiente encabezado parece funcionar igual de bien:
public static <T> void fill(List<T> list, T obj)No puedo ver una razón por la cual se necesita el comodín; un código como el siguiente funciona tanto con el segundo encabezado como con el primero:
List<Number> nums = new ArrayList<>(); Integer i = 43; fill(nums, i); //fill method written using second header Mi pregunta es: ¿Para qué llamada específica de fill funcionaría el primer encabezado pero no el segundo? Y si no existe tal llamada, ¿por qué incluir el comodín? En este caso, el comodín no hace que el método sea más conciso ni aumenta la legibilidad (en mi opinión).
Para su ejemplo, la razón por la que 'funciona' con su firma básica <T> es que un número entero también es un número. La única 'T' que funciona es T = Number , y luego todo funciona.
En este caso, la expresión que tiene para el parámetro T obj es un tipo cosificado: tiene un Integer . Podrías tener una T en su lugar. Quizás tengas esto:
class AtomicReference<T> { // The actual impl of juconcurrent.AtomicReference... // but with this one additional method: public void fillIntoList(List<? super T> list) { T currentValue = get(); Collections.fill(list, currentValue); } }Quizá quiera escribir algo como esto:
AtomicReference<String> ref = new AtomicReference<String>("hello"); List<CharSequence> texts = new ArrayList<>(); ... ref.fillIntoList(texts); Si mi método hipotético fillIntoList simplemente tuviera List<T> en la firma que no se compilaría. Afortunadamente lo hace, por lo que el código se compila. ¿No había hecho el método Collections.fill el <? super T> cosa <? super T> , la invocación del método Collections.fill en mi método fillIntoList habría fallado.
Es muy exótico que surja algo de esto. Pero puede surgir. List<? super T> es la firma estrictamente superior aquí: puede hacer todo lo que hace List<T> , y más, y también es semánticamente correcto: por supuesto, puedo completar una lista de foos escribiendo en cada ranura una referencia a algo que se que es un bar, si bar es hijo de foo.
Eso es porque la herencia es útil en algunos casos.
Por ejemplo, si tiene la siguiente estructura de clases:
public class Parent { //some code } public class Child extends Parent { //some another code }Podrías usar el primer método escribiendo:
List<Child> children = new ArrayList<>(); Parent otherParentObject = new Parent(); //after this line, set the values for the class List<Parent> outParentList = new ArrayList<>(); fill(children, otherParentObject); //fill method using first signature;