The syntax for Java generics bounded wildcards, representing the unknown type by ? is:

As a rule of thumb, you should use

Using extends or super is usually better because it makes your code more flexible (as in: allowing the use of subtypes and supertypes), as you will see below.

class Shoe {}
class IPhone {}
interface Fruit {}
class Apple implements Fruit {}
class Banana implements Fruit {}
class GrannySmith extends Apple {}

   public class FruitHelper {

        public void eatAll(Collection<? extends Fruit> fruits) {}

        public void addApple(Collection<? super Apple> apples) {}
}

The compiler will now be able to detect certain bad usage:

public class GenericsTest {
     public static void main(String[] args){
 FruitHelper fruitHelper = new FruitHelper() ;
   List<Fruit> fruits = new ArrayList<Fruit>();
   fruits.add(new Apple()); // Allowed, as Apple is a Fruit
   fruits.add(new Banana()); // Allowed, as Banana is a Fruit
   fruitHelper.addApple(fruits); // Allowed, as "Fruit super Apple"
   fruitHelper.eatAll(fruits); // Allowed

   Collection<Banana> bananas = new ArrayList<>();
   bananas.add(new Banana()); // Allowed
   //fruitHelper.addApple(bananas); // Compile error: may only contain Bananas!
   fruitHelper.eatAll(bananas); // Allowed, as all Bananas are Fruits

   Collection<Apple> apples = new ArrayList<>();
   fruitHelper.addApple(apples); // Allowed
   apples.add(new GrannySmith()); // Allowed, as this is an Apple
   fruitHelper.eatAll(apples); // Allowed, as all Apples are Fruits.
   
   Collection<GrannySmith> grannySmithApples = new ArrayList<>();
   fruitHelper.addApple(grannySmithApples); //Compile error: Not allowed.
                                  // GrannySmith is not a supertype of Apple
   apples.add(new GrannySmith()); //Still allowed, GrannySmith is an Apple
   fruitHelper.eatAll(grannySmithApples);//Still allowed, GrannySmith is a Fruit

   Collection<Object> objects = new ArrayList<>();
   fruitHelper.addApple(objects); // Allowed, as Object super Apple
   objects.add(new Shoe()); // Not a fruit
   objects.add(new IPhone()); // Not a fruit
   //fruitHelper.eatAll(objects); // Compile error: may contain a Shoe, too!
}

Choosing the right T, ? super T or ? extends T is necessary to allow the use with subtypes. The compiler can then ensure type safety; you should not need to cast (which is not type safe, and may cause programming errors) if you use them properly.

If it is not easy to understand, please remember PECS rule:

Producer uses “Extends” and Consumer uses “Super”.

(Producer has only write access, and Consumer has only read access)