Bounded Generic Types & Wildcards - [OOP & Java #6]
Tags: computerscience, java, oop
Published: 2020-09-22 13:14:43

Continuing on the topic of Generics:
- Bounded generic types
```java
<T extends String> void print(T t){}
```
- Wildcards
```java
Drawer<?> drawer = new Drawer<String>("abc"1);
```

---
### Bounded Generic Types
Think of the word "bounded" literally - limit something to a range of possible values.

Now link back to the point made in the [previous article](https://dev.to/tlylt/generics-oop-java-5-2k46) that whenever we use Generics, we still want as much compile-time type checking as possible. This is a helpful strategy because compile-time errors are easier to catch than run-time errors. If we make sure the right type of objects are being passed around, we have one less thing to worry about after compilation.

```java
// for illustration purposes
class Clothing {};
class Shirt extends Clothing {};
class Tshirt extends Shirt {};

class Drawer<T> {
  T obj;
  Drawer(T obj) {
    this.obj = obj;
  }
  T get() {
    return this.obj;
  }
}
```
Reusing the example of a `Drawer` object, and also classes that represent `Clothing`, `Shirt` and `Tshirt`, let's take a look at how bounded type parameters work.

The original implementation is without the bound. The placeholder `T` can technically take in any possible type of object.
```java
new Drawer<Clothing>(new Clothing());
new Drawer<String>(new String());
new Drawer<Double>(new Double());
```
However, this may not be ideal because
> There may be times when you want to restrict the types that can be used as type arguments in a parameterized type.

Thinking along the line of OOP, we may not want our `Drawer` to take in something that does not belong (e.g. a Drawer of `Human`?!). So, we may very well intend to bound the placeholder to an upper bound such as `Clothing` and therefore only accept substitutions of the stated bound and any of the subclasses. For example, Since `<T extends Clothing>`, `T` can be substituted with `Clothing` or `Shirt` or even `Tshirt` (Because both `Shirt` and `Tshirt` are `Clothing`, by way of inheritance relationship).

```java
class Drawer<T extends Clothing> {
  T obj;
  Drawer(T obj) {
    this.obj = obj;
  }
  T get() {
    return this.obj;
  }
}

// usage
new Drawer<Double>(new Double()); // ERROR
new Drawer<Clothing>(new Clothing()); // SAFE
new Drawer<Shirt>(new Shirt()); // SAFE
new Drawer<Tshirt>(new Tshirt()); // SAFE
```
Bounded Generic types are implemented by using the `extends` keyword. Note that multiple bounds are also possible.
```java 
// has the same effect of only extending Clothing
Drawer<T extends Clothing & Shirt & Tshirt> {
  T obj;
  Drawer(T obj) {
    this.obj = obj;
  }
  T get() {
    return this.obj;
  }
}
```
Another point on bounded type is that bounds increase the number of permitted method calls during compile-time. Without the bound, the compiler only knows that a placeholder can be one of the possible types. Therefore, calling any methods on an object with a placeholder type cannot be guaranteed to succeed. 

Imagine you specified a method that takes in an argument of `T t` and you tried writing `t.length()`. Not all objects have `length()` method and hence it is unsafe for the compiler to compile the code. UNLESS, given that all objects are subclasses of `Object`, any object in Java will support the methods specified in the `Object` class. This means calling `toString()` or `equals()` on `t` are fine.

With bounded types, now the compiler is aware that the object going into the placeholder is going to be one of the types/subtypes. Then, it is safe to call any methods specified in the bound.

---
### Wildcards
Before we delve into wildcards, we need to review the concept of complex vs simple types
```java
// examples of simple
String s;
Clothing c;
Shirt st;

// examples of complex
String[] s;
Clothing[] c;
Shirt[] st;
```
Java arrays are complex and because arrays are covariant, we get the following characteristics:
```java
Clothing c = new Shirt(); // SAFE
Clothing[] c = new Shirt[1](); // SAFE
```
When we use the example of a `Drawer`, we are also working with this notion of a container. So, just like arrays that store items into a collection, does the following work?
```java
Clothing c = new Shirt();
Drawer<Clothing> c = new Drawer<Shirt>(new Shirt());
```
Previously, we mentioned that Generics are invariant, meaning the subtyping relationship of simple types does not extend to complex types. This is also why we can omit the type on the Right-Hand-Side of the equal sign.
```java
// SAFE
Drawer<Clothing> c = new Drawer<Clothing>(new Clothing); 
// ALSO SAFE
Drawer<Clothing> c = new Drawer<>(new Clothing): 
```
If the discussion ended here, then we will not need wildcards. However, the subtyping relationship seems reasonable enough. Given that we have it for Java arrays and they are kind of helpful in many circumstances to support polymorphism, we can make use of wildcards to achieve the same effects for Generics.

A drawer for clothes is ultimately also a drawer for shirts.
We will use wildcards, or expressed as `?`, to workaround the restriction.
```java
// SAFE
Drawer<?> d = new Drawer<Clothing>(new Clothing); 
d = new Drawer<Shirt>(new Shirt());

// ALSO SAFE
Drawer<Shirt> s = new Drawer<Shirt>(new Shirt());
Drawer<?> anyDrawer = s;
```
For understanding, the `?` can be thought of as "ANY". So, any drawer can be a drawer for clothing, and any drawer can also be a drawer for shirts.

### Bounded wildcards
When we do use `<?>`, we lose the number of method calls we can perform when we take things out of the drawer. Because the drawer might be of any kind, the only guarantee we get of the things that come out of the drawer is that they are of type `Object`. Therefore, we can only call `Object` level methods such as `toString()` or `equals()`. A familiar phrase?

Adding a bound to wildcards gives us the ability to first restrict the range of types that can go into the placeholder, and then increase the number of permitted method calls.
```java
// upper-bounded wildcard
// SAFE
Drawer<? extends Shirt> drawerOfShirt;
drawerOfShirt = new Drawer<Shirt>(new Shirt());
Shirt s = drawerOfShirt.get();
drawerOfShirt = new Drawer<Tshirt>(new Tshirt());
Shirt s = drawerOfShirt.get();

// ERROR
drawerOfShirt = new Drawer<Clothing>(new Clothing());
Shirt s = drawerOfShirt.get();
```
`Drawer<? extends Shirt>` allows only objects of type `Shirt` and any of the sub-types. This is quite reasonable because we can imagine a drawer of T-shirts is still a drawer of shirts. Hence when we take things out of the drawer, it can be a T-shirt, which is still a shirt.

Since `Clothing` is not of type `Shirt` or a child of `Shirt`, by the restriction of bound it will not work. Logically, we cannot allow it to be true because if a pair of pants is taken out of the `Drawer<Clothing>`, it is still a piece of clothing, but it is not a shirt.

We can also do the same for a lower-bound using the keyword `super`.
```java
// suppose Drawer contains an update method
class Drawer<T> {
  T obj;
  //...
  void update(T obj) {
    this.obj = obj;
  }
}

// lower-bounded wildcard
// SAFE
Drawer<? super Shirt> drawer= new Drawer<Shirt>(new Shirt());
drawer.update(new Shirt());
drawer = new Drawer<Clothing>(new Clothing);
drawer.update(new Shirt()); // still safe
```
Now the drawer can be a drawer for `Shirt`, `Clothing`, and even `Object`. 

A shirt can go into the drawer no matter which reference is it pointing at. This is because a `Shirt` is a piece of clothing and going into a drawer of clothing is also reasonable. 

---
### Get And Put principle
Some observations:
#### Covariance
- `Shirt` is a subtype of `Clothing` 
- `Drawer<Shirt>` is a subtype of `Drawer<? extends Clothing>`

#### Contravariance
- `Shirt` is a subtype of `Clothing` 
- `Drawer<Clothing>` is a subtype of `Drawer<? super Shirt>`

---
#### Discussion on the `equals` method
A typical `@Override` of `equals` method in Java:
- Check if the argument object is exactly the same object as itself
- If it is of the same type, perform customized comparison based on certain methods available, require typecasting before calling available methods
- If not of the same type, conclude not equal
```java
// suppose a string is equal to another string if 
// they have the same length
@Override
public boolean equals(Object obj) {
  if (this == obj) {
    return true;
  } else if (obj instanceof String) {
    String s = (String) obj;
    return this.length() == s.length();
  } else {
    return false;
  }
}
```
With Generics, it is logical to follow up with the following implementation that is not exactly correct.
```java
// suppose a drawer is equal to another drawer if
// they have the same content
@Override
public boolean equals(Object obj) {
  if (this == obj) {
    return true;
  } else if (obj instanceof Drawer<T>) {
    Drawer<T> anyDrawer = (Drawer<T>) obj;
    return this.get().equals(anyDrawer.get());
  } else {
    return false;
  }
}
```
We have two errors for the above code:
- illegal generic type for `instanceof`
 - Generic types are not accessible during runtime due to type erasure, which is the fact that generic types are removed after checking. Suffice to say that during runtime, `Drawer<Clothing>` is the same as `Drawer` without the type (also known as raw type). The reason for this is to allow backward compatibility with Java code that existed before the introduction of Generics. Read more about this [here](http://www.angelikalanger.com/GenericsFAQ/FAQSections/ParameterizedTypes.html)
- unchecked or unsafe operations
 - The previous error tells us that we cannot check if the object being compared with is of type `Drawer<T>`, T here can be thought of as a specific type. If the `if` condition is valid and we do a cast
```java
Drawer<T> otherDrawer = (Drawer<T>) obj;
```
The Right-Hand-Side can be thought of as casting an `Object` to the raw type `Drawer` instead of `Drawer<T>` (again because of type erasure). This can be dangerous because suppose we have `Drawer<Stationary>`, then we know that it is of type `Drawer` by doing `instanceof Drawer`. Now if we cast it to `Drawer<T>` when T is `Clothing`, the compiler might just go on and do the cast because to the compiler, we are casting `Drawer` to `Drawer`. However, we have `Drawer<Clothing> otherDrawer` pointing to `Drawer<Stationary>`, which is not OK. Note that we did not use any bounds so standard invariant characteristics of Generics should apply.

The solution here is to use wildcards.
```java
@Override
public boolean equals(Object obj) {
  if (this == obj) {
    return true;
  } else if (obj instanceof Drawer) {
    Drawer<?> anyDrawer = (Drawer<?>) obj;
    return this.get().equals(anyDrawer.get());
  } else {
    return false;
  }
}
```

The reason why it works now is that `?` here means any type ranging from `Object` to any of your custom types. Saying that it casts to **any** type is slightly different from saying that it casts to **a specific** type. It is safe to say that a specific drawer is one of the possible drawers. Any of the possible drawers may not be that one specific drawer. 

Again, focusing on the Right-Hand-Side, we know that `obj` is `instanceof` `Drawer`. We have no idea it is a drawer of what. But in the cast, we let it cast to a Drawer that can contain any possible types, which is almost similar to saying that "let it cast to something, as long as that something is a Drawer". This is a reasonable conversion. The compiler won't complain now because you are being very general here. The side effect is that after casting, you can only call the most common methods (`Object` level methods such as `toString()` and `equals()`) on `anyDrawer`. 

### Exercises
For the following statements, do they compile?
```java
// for illustration purposes
class Clothing {};
class Shirt extends Clothing {};
class Tshirt extends Shirt {};

class Drawer<T> {
  Drawer(){};
}

1. Drawer<? extends Shirt> s = new Drawer<Tshirt>();
2. Drawer<? super Clothing> s = new Drawer<Shirt>();
3. Drawer<?> s = new Drawer<Double>();
4. Drawer<Clothing> s = new Drawer<>();
5. Drawer<Shirt> s = new Drawer<Clothing>();
6a. Drawer<Tshirt> ts = new Drawer<Tshirt>();
6b. ts = new Drawer<Shirt>();
7a. Drawer<?> ws = new Drawer<Clothing>();
7b. ws = new Drawer<Tshirt>();
```

---
![try-gif](https://media.giphy.com/media/XdreKrQI1LjcQ/giphy.gif)

---
#### Answers as follows:
1. Yes  6a. Yes
2. No   6b. No
3. Yes  7a. Yes
4. Yes  7b. Yes
5. No

---
### Closing thoughts
A lot to cover in this article and there's more left unmentioned. I think the understanding of Generics will come with increased contact. I did not go into detail about type erasure and raw types. Also, explaining Generics in the context of Java Collections Framework is possibly very helpful and I do intend to talk about that later on. 

One resource for further reading is [Java Generics FAQs](http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html)