Java Generics wildcards
Topics
• Wildcards with extends
• E.g. List<? extends Number>
• Wildcards with super
• E.g. List<? super Integer>
• The Get and Put Principle
Wildcards with extends
Wildcards with extends: what is it?
// a List which contains elements whose type is a subtype of Number
List<? extends Number> list_extendsNumber;
// valid:
list_extendsNumber = new ArrayList<Number>();
list_extendsNumber = new ArrayList<Integer>(); // Integer extends Number
list_extendsNumber = new ArrayList<BigDecimal>(); // BigDecimal extends Number
// invalid:
list_extendsNumber = new ArrayList<String>(); // compilation error
// a String is not a Number
list_extendsNumber = new ArrayList<Object>(); // compilation error
// Number extends Object
// but not the other way around
// It's guaranteed that it contains a Number or its subtype
Number firstNumber = list_extendsNumber.get(0);
Wildcards with extends: how can we use it?
// A method which accepts any List whose type parameter is a subtype of Number
double avg(List<? extends Number> nums) {
return nums.stream().mapToDouble(Number::doubleValue).average().orElse(0);
}
// valid:
List<Number> nums = Arrays.asList(1, 0.5d, 3);
assert avg(nums) == 1.5d;
List<Integer> ints = Arrays.asList(1, 2, 3);
assert avg(ints) == 2d;
List<BigDecimal> bds = Arrays.asList(new BigDecimal(1), new BigDecimal(2), new BigDecimal(3));
assert avg(bds) == 2d;
// invalid:
List<String> strs = Arrays.asList("foo", "bar", "baz");
avg(strs) // compilation error; a String is not a Number
List<Object> objs = Arrays.asList("foo", 0.5d, 3);
avg(objs) // compilation error; Number extends Object but not the other way around
Wildcards with extends:what’s good about it?
// No wildcard
double avg(List<Number> nums) {
return nums.stream().mapToDouble(Number::doubleValue).average().orElse(0);
}
// valid:
List<Number> nums = Arrays.asList(1, 0.5d, 3);
assert avg(nums) == 1.5d; // still fine
// not valid anymore:
List<Integer> ints = Arrays.asList(1, 2, 3);
assert avg(ints) == 2d; // compilation error
List<BigDecimal> bds = Arrays.asList(new BigDecimal(1), new BigDecimal(2), new BigDecimal(3));
assert avg(bds) == 2d; // compilation error
Wildcards with extends provide more flexibility to code which
gets values out of a container object (e.g. List)
Wildcards with extends: limitation
List<? extends Number> list_extendsNumber = new ArrayList<Integer>();
list_extendsNumber.add(null); // compiles; null is the only exception
list_extendsNumber.add(1); // compilation error
// why? remember we can assign the following too
list_extendsNumber = new ArrayList<BigDecimal>(); // it accepts only BigDecimal. not Integer
You won't be able to put any value except for null
through a variable using extends.
Wildcards with super
Wildcards with super: what is it?
// a List which contains elements whose type is a supertype of Integer
List<? super Integer> list_superInteger;
// valid:
list_superInteger = new ArrayList<Integer>();
list_superInteger = new ArrayList<Number>(); // Number is a supertype of Integer
list_superInteger = new ArrayList<Object>(); // Object is a supertype of Integer
// invalid:
list_superInteger = new ArrayList<String>(); // compilation error;
// String is not a supertype of Integer
// It's guaranteed that it can accept an Integer
list_superInteger.put(123);
Wildcards with super: how can we use it?
// A method which accepts any List whose type parameter is a supertype of Integer
void addInts(List<? super Integer> ints) {
Collections.addAll(ints, 1, 2, 3);
}
// valid:
List<Integer> ints = new ArrayList<>();
addInts(ints);
assert ints.toString().equals("[1, 2, 3]");
List<Number> nums = new ArrayList<>();
addInts(nums);
assert nums.toString().equals("[1, 2, 3]");
List<Object> objs = new ArrayList<>();
addInts(objs);
assert objs.toString().equals("[1, 2, 3]");
// invalid:
List<String> strs = new ArrayList<>();
addInts(strs); // compilation error; List<String> cannot accept an Integer
Wildcards with super: what’s good about it?
// No wildcard
void addInts(List<Integer> ints) {
Collections.addAll(ints, 1, 2, 3);
}
// valid:
List<Integer> ints = new ArrayList<>();
addInts(ints);
assert ints.toString().equals("[1, 2, 3]");
// not valid anymore:
List<Number> nums = new ArrayList<>();
addInts(nums); // compilation error
List<Object> objs = new ArrayList<>();
addInts(objs); // compilation error
Wildcards with super provide more flexibility to code which
puts values into a container object (e.g. List).
Wildcards with super: limitation
List<Integer> ints = new ArrayList<>();
ints.add(123);
List<? super Integer> list_superInteger = ints;
Object head = list_superInteger.get(0); // only Object can be used
// as the type of head
assert head.equals(123);
// we cannot do this:
Integer head = list_superInteger.get(0); // compilation error
// why? remember we can assign the following too
list_superInteger = new ArrayList<Object>(); // there is no guarantee that
// it contains an Integer
The only type we can use to get a value out of a variable using super is
the Object type.
The Get and Put Principle
“Use an extends wildcard when you only get values out of a structure,
use a super wildcard when you only put values into a structure, and
don't use a wildcard when you both get and put.”
- Naftalin, M., Wadler, P. (2007). Java Generics And Collections, O'Reilly.
p.19
The Get and Put Principle: an example with
functional interfaces
// Supplier: you only get values out of it
// Consumer: you only put values into it
void myMethod(Supplier<? extends Number> numberSupplier, Consumer<? super String> stringConsumer) {
Number number = numberSupplier.get();
String result = "I got a number whose value in double is: " + number.doubleValue();
stringConsumer.accept(result);
}
// client code: parameters don’t necessarily have to be Supplier<Number> and Consumer<String>
Supplier<BigDecimal> bigDecimalSupplier = () -> new BigDecimal("0.5");
AtomicReference<Object> reference = new AtomicReference<>();
Consumer<Object> objectConsumer = reference::set;
myMethod(bigDecimalSupplier, objectConsumer);
assert reference.get().equals("I got a number whose value in double is: 0.5");
Conclusion
• We’ve discussed the following topics about Generics wildcards:
• Usage
• Benefits
• Limitations
• When to use
• Java Generics wildcards make Java code more flexible and type-safe.
• When you write a method or a constructor which receives an object
that has a type parameter, think if you can apply one.

Java Generics wildcards

  • 1.
  • 2.
    Topics • Wildcards withextends • E.g. List<? extends Number> • Wildcards with super • E.g. List<? super Integer> • The Get and Put Principle
  • 3.
  • 4.
    Wildcards with extends:what is it? // a List which contains elements whose type is a subtype of Number List<? extends Number> list_extendsNumber; // valid: list_extendsNumber = new ArrayList<Number>(); list_extendsNumber = new ArrayList<Integer>(); // Integer extends Number list_extendsNumber = new ArrayList<BigDecimal>(); // BigDecimal extends Number // invalid: list_extendsNumber = new ArrayList<String>(); // compilation error // a String is not a Number list_extendsNumber = new ArrayList<Object>(); // compilation error // Number extends Object // but not the other way around // It's guaranteed that it contains a Number or its subtype Number firstNumber = list_extendsNumber.get(0);
  • 5.
    Wildcards with extends:how can we use it? // A method which accepts any List whose type parameter is a subtype of Number double avg(List<? extends Number> nums) { return nums.stream().mapToDouble(Number::doubleValue).average().orElse(0); } // valid: List<Number> nums = Arrays.asList(1, 0.5d, 3); assert avg(nums) == 1.5d; List<Integer> ints = Arrays.asList(1, 2, 3); assert avg(ints) == 2d; List<BigDecimal> bds = Arrays.asList(new BigDecimal(1), new BigDecimal(2), new BigDecimal(3)); assert avg(bds) == 2d; // invalid: List<String> strs = Arrays.asList("foo", "bar", "baz"); avg(strs) // compilation error; a String is not a Number List<Object> objs = Arrays.asList("foo", 0.5d, 3); avg(objs) // compilation error; Number extends Object but not the other way around
  • 6.
    Wildcards with extends:what’sgood about it? // No wildcard double avg(List<Number> nums) { return nums.stream().mapToDouble(Number::doubleValue).average().orElse(0); } // valid: List<Number> nums = Arrays.asList(1, 0.5d, 3); assert avg(nums) == 1.5d; // still fine // not valid anymore: List<Integer> ints = Arrays.asList(1, 2, 3); assert avg(ints) == 2d; // compilation error List<BigDecimal> bds = Arrays.asList(new BigDecimal(1), new BigDecimal(2), new BigDecimal(3)); assert avg(bds) == 2d; // compilation error Wildcards with extends provide more flexibility to code which gets values out of a container object (e.g. List)
  • 7.
    Wildcards with extends:limitation List<? extends Number> list_extendsNumber = new ArrayList<Integer>(); list_extendsNumber.add(null); // compiles; null is the only exception list_extendsNumber.add(1); // compilation error // why? remember we can assign the following too list_extendsNumber = new ArrayList<BigDecimal>(); // it accepts only BigDecimal. not Integer You won't be able to put any value except for null through a variable using extends.
  • 8.
  • 9.
    Wildcards with super:what is it? // a List which contains elements whose type is a supertype of Integer List<? super Integer> list_superInteger; // valid: list_superInteger = new ArrayList<Integer>(); list_superInteger = new ArrayList<Number>(); // Number is a supertype of Integer list_superInteger = new ArrayList<Object>(); // Object is a supertype of Integer // invalid: list_superInteger = new ArrayList<String>(); // compilation error; // String is not a supertype of Integer // It's guaranteed that it can accept an Integer list_superInteger.put(123);
  • 10.
    Wildcards with super:how can we use it? // A method which accepts any List whose type parameter is a supertype of Integer void addInts(List<? super Integer> ints) { Collections.addAll(ints, 1, 2, 3); } // valid: List<Integer> ints = new ArrayList<>(); addInts(ints); assert ints.toString().equals("[1, 2, 3]"); List<Number> nums = new ArrayList<>(); addInts(nums); assert nums.toString().equals("[1, 2, 3]"); List<Object> objs = new ArrayList<>(); addInts(objs); assert objs.toString().equals("[1, 2, 3]"); // invalid: List<String> strs = new ArrayList<>(); addInts(strs); // compilation error; List<String> cannot accept an Integer
  • 11.
    Wildcards with super:what’s good about it? // No wildcard void addInts(List<Integer> ints) { Collections.addAll(ints, 1, 2, 3); } // valid: List<Integer> ints = new ArrayList<>(); addInts(ints); assert ints.toString().equals("[1, 2, 3]"); // not valid anymore: List<Number> nums = new ArrayList<>(); addInts(nums); // compilation error List<Object> objs = new ArrayList<>(); addInts(objs); // compilation error Wildcards with super provide more flexibility to code which puts values into a container object (e.g. List).
  • 12.
    Wildcards with super:limitation List<Integer> ints = new ArrayList<>(); ints.add(123); List<? super Integer> list_superInteger = ints; Object head = list_superInteger.get(0); // only Object can be used // as the type of head assert head.equals(123); // we cannot do this: Integer head = list_superInteger.get(0); // compilation error // why? remember we can assign the following too list_superInteger = new ArrayList<Object>(); // there is no guarantee that // it contains an Integer The only type we can use to get a value out of a variable using super is the Object type.
  • 13.
    The Get andPut Principle “Use an extends wildcard when you only get values out of a structure, use a super wildcard when you only put values into a structure, and don't use a wildcard when you both get and put.” - Naftalin, M., Wadler, P. (2007). Java Generics And Collections, O'Reilly. p.19
  • 14.
    The Get andPut Principle: an example with functional interfaces // Supplier: you only get values out of it // Consumer: you only put values into it void myMethod(Supplier<? extends Number> numberSupplier, Consumer<? super String> stringConsumer) { Number number = numberSupplier.get(); String result = "I got a number whose value in double is: " + number.doubleValue(); stringConsumer.accept(result); } // client code: parameters don’t necessarily have to be Supplier<Number> and Consumer<String> Supplier<BigDecimal> bigDecimalSupplier = () -> new BigDecimal("0.5"); AtomicReference<Object> reference = new AtomicReference<>(); Consumer<Object> objectConsumer = reference::set; myMethod(bigDecimalSupplier, objectConsumer); assert reference.get().equals("I got a number whose value in double is: 0.5");
  • 15.
    Conclusion • We’ve discussedthe following topics about Generics wildcards: • Usage • Benefits • Limitations • When to use • Java Generics wildcards make Java code more flexible and type-safe. • When you write a method or a constructor which receives an object that has a type parameter, think if you can apply one.