Understanding limited generics in java. What's the point?
I am trying to understand bounded types and am not quite clear on their point.
There is an example of restricted generics at https://docs.oracle.com/javase/tutorial/java/generics/bounded.html that provides this use case:
public class NaturalNumber<T extends Integer> {
private T n;
public NaturalNumber(T n) { this.n = n; }
public boolean isEven() {
return n.intValue() % 2 == 0;
}
// ...
}
If you are going to restrict the classes that can be a parameterized type, why not just forget the parameterization all together and have:
public class NaturalNumber {
private Integer n;
public NaturalNumber(Integer n) { this.n = n; }
public boolean isEven() {
return n.intValue() % 2 == 0;
}
// ...
}
Then any class that extends / implements Integer can be used with that class.
It also begs the question: How does T expand Integer in the first example when the Java Integer class is final?
Thank.
source to share
How does T expand Integer in the first example when the Java Integer class is final?
T
perhaps only Integer
, therefore, the "continuation" here is purely symbolic. (I start with a side note because, indeed, this is an example where generics are useless. I really don't know why the tutorial thinks this is an informative demo. It is not.)
Suppose instead that T extends Number
:
class Example<T extends Number> {
private T num;
void setNum(T num) { this.num = num; }
T getNum() { return num; }
}
So the point in common in the general case is that you can do this:
Example<Integer> e = new Example<>();
e.setNum( Integer.valueOf(10) );
// returning num as Integer
Integer i = e.getNum();
// and this won't compile
e.setNum( Double.valueOf(10.0) );
Generics is a form of parametric polymorphism , in fact, it allows us to reuse code with general information about types.
So what's the point of the border?
Linked here means T
must be Number
or subclass Number
, so we can call methods Number
on the instance T
. Number
unfortunately is usually a useless base class on its own (due to precision problems), but it can allow us to do something interesting, for example:
class Example<T extends Number> extends Number {
// ^^^^^^^^^^^^^^
...
@Override
public int intValue() {
return num.intValue();
}
// and so on
}
It is more common, for example, to find T extends Comparable<T>
what allows us to do something more meaningful with T
. We might have something like:
// T must be a subclass of Number
// AND implement Comparable
Example<T extends Number & Comparable<T>>
implements Comparable<Example<T>> {
...
@Override
public int compareTo(Example<T> that) {
return this.num.compareTo(that.num);
}
}
And now our class Example
has a natural order. We can sort it even if we don't know what T
really is inside the class body.
If we combine these concepts, then:
- generics allow the "outside world" to specify the actual type and
- boundaries allow the "inner world" to use the community,
we could build constructs like:
static <T extends Comparable<T>> T min(T a, T b) {
return (a.compareTo(b) < 0) ? a : b;
}
{
// returns "x"
String s = min("x", "z");
// returns -1
Integer i = min(1, -1);
}
source to share