|
1 | 1 | # Java Variance |
2 | 2 |
|
3 | | -  |
| 3 | +  |
4 | 4 |
|
5 | 5 | ## Description |
6 | 6 |
|
7 | | -This is a project that incorporates Java annotations in order to allow specification of different generic parameter variances. |
| 7 | +This project provides Java annotations that enable fine-grained specification of variance for class generics, improving flexibility in generic programming. |
8 | 8 |
|
9 | 9 | ## Table of Contents |
10 | 10 |
|
11 | 11 | - [Installation](#installation) |
12 | | - - [Prerequisites](#prerequisites) |
13 | 12 | - [For maven users](#for-maven-users) |
14 | | - - [For grade users](#for-gradle-users) |
15 | 13 | - [For other users](#for-other-users) |
| 14 | +- [Background](#background) |
| 15 | + - [What is variance](#what-is-variance) |
| 16 | + - [Types of variance](#types-of-variance) |
16 | 17 | - [Usage](#usage) |
17 | | - - [Subsection](#subsection) |
| 18 | + - [Annotations](#annotations) |
| 19 | + - [Output](#output) |
18 | 20 | - [Contributing](#contributing) |
19 | 21 |
|
20 | 22 | ## Installation |
21 | 23 |
|
22 | | -### Prerequisites |
| 24 | +### For Maven users |
23 | 25 |
|
24 | | -### For maven users |
| 26 | +Add the following dependency to your pom.xml |
25 | 27 |
|
26 | | -<!-- TODO: Add when project is published to maven central --> |
| 28 | +```xml |
| 29 | +<dependency> |
| 30 | + <groupId>io.github.bldl</groupId> |
| 31 | + <artifactId>java-variance</artifactId> |
| 32 | + <version>LATEST</version> |
| 33 | +</dependency> |
| 34 | +``` |
27 | 35 |
|
28 | | -### For gradle users |
| 36 | +### For other users |
29 | 37 |
|
30 | | -<!-- TODO: Add when project is published to maven central --> |
| 38 | +Using Git Submodules |
| 39 | +If your project does not use Maven or Gradle, but you still want to use the annotations provided by this library, you can include it as a Git submodule. Here's how: |
31 | 40 |
|
32 | | -### For other users |
| 41 | +**1. Add the repository as a submodule:** |
33 | 42 |
|
34 | | -```bash |
35 | | -$ git submodule add https://github.com/bldl/java-variance |
| 43 | +```sh |
| 44 | +git submodule add https://github.com/bldl/java-variance |
36 | 45 | ``` |
37 | 46 |
|
38 | | -## What is variance |
| 47 | +**2. Initialize and update the submodule:** |
| 48 | + |
| 49 | +```sh |
| 50 | +git submodule update --init |
| 51 | +``` |
| 52 | + |
| 53 | +**3. Add the submodule to your classpath in your project setup. For example:** |
| 54 | + |
| 55 | +- If using an IDE, configure the submodule to be a source directory. |
| 56 | +- If using a custom build script, ensure the submodule's output is included in the compilation step. |
39 | 57 |
|
40 | | -# Variance in Generic Programming |
| 58 | +## Background |
41 | 59 |
|
42 | | -**Variance** in Generic Programming refers to how subtyping between more complex generic types (like `List<T>`) relates to subtyping between their type parameters (like `T`). It determines whether type relationships are preserved, reversed, or invariant when generic types are involved. |
| 60 | +### What is variance |
43 | 61 |
|
44 | | -## Types of Variance |
| 62 | +**Variance** in Generic Programming refers to how subtyping between more complex generic types (like `List<T>`) relates to subtyping between their type parameters (like `T`). It determines whether type relationships are preserved, reversed, or discarded for each type parameter. |
45 | 63 |
|
46 | | -### 1. **Covariance** |
| 64 | +### Types of Variance |
| 65 | + |
| 66 | +**1. Covariance** |
47 | 67 |
|
48 | 68 | Covariance allows a generic type to be substituted with another generic type that has a more specific (derived) type parameter. |
49 | 69 |
|
50 | | -- Example: `List<Animal>` can accept `List<Dog>` if `Dog` is a subtype of `Animal`. |
51 | | -- Used when a generic type is only **producing** (out) values. |
52 | | -- Marked with **`out`** in Kotlin or achieved with `? extends` in Java. |
| 70 | +- Example: `List<Animal>` is a subtype of `List<Dog>` if `Dog` is a subtype of `Animal`. |
| 71 | +- Used when a generic type is only **producing** (out) values. That is the generic type is not part of any non-private variables or method parameters. |
53 | 72 |
|
54 | | -### 2. **Contravariance** |
| 73 | +**2. Contravariance** |
55 | 74 |
|
56 | 75 | Contravariance allows a generic type to be substituted with another generic type that has a more general (base) type parameter. |
57 | 76 |
|
58 | | -- Example: `List<Dog>` can accept `List<Animal>` if `Animal` is a supertype of `Dog`. |
59 | | -- Used when a generic type is only **consuming** (in) values. |
60 | | -- Marked with **`in`** in Kotlin or achieved with `? super` in Java. |
| 77 | +- Example: `List<Dog>` is a sybtype of `List<Animal>` if `Animal` is a supertype of `Dog`. |
| 78 | +- Used when a generic type is only **consuming** (in) values. That is the generic type is not part of any non-private variables or method return types. |
| 79 | + |
| 80 | +**3. Bivariance** |
| 81 | + |
| 82 | +Bivariance allows a generic type to be substituted with another generic type regardless of the relationship between their type parameters. This means a generic type can accept both more specific (derived) and more general (base) type parameters interchangeably. |
| 83 | + |
| 84 | +- Example: Both `List<Dog>` is a sybtype of `List<Animal>` and `List<Animal>` is a subtype of of `List<Dog>` if Dog is a subtype of Animal. |
| 85 | +- Practical Use: Bivariance is generally unsafe in strongly typed systems because it disregards type safety. It might be allowed in certain scenarios where type constraints are relaxed for specific purposes, such as event handlers or certain dynamic language constructs. |
| 86 | +- Drawbacks: Since it permits type substitution in both directions, it can lead to runtime errors if the system tries to enforce incompatible operations. |
61 | 87 |
|
62 | | -### 3. **Invariance** |
| 88 | +**4. Invariance** |
63 | 89 |
|
64 | 90 | Invariance means no substitution is allowed between different generic types, even if their type parameters have a subtype relationship. |
65 | 91 |
|
66 | 92 | - Example: `List<Animal>` and `List<Dog>` are entirely distinct and incompatible. |
67 | 93 | - This is the default in many languages, like Java's generics. |
68 | 94 |
|
69 | | ---- |
70 | | - |
71 | | -## Key Insight |
72 | | - |
73 | | -Covariance and contravariance depend on how the generic type uses its type parameter: **producing outputs** (covariance) or **consuming inputs** (contravariance). Invariant types neither allow flexibility. |
| 95 | +## Usage |
74 | 96 |
|
75 | 97 | ### Annotations |
76 | 98 |
|
77 | | -There are currently three annotations provided by this project: MyVariance, Covariant and Contravariant. With these you are able to annotate type parameters for classes in order to |
| 99 | +There are currently three annotations provided by this project: MyVariance, Covariant and Contravariant. With these you are able to annotate type parameters for classes in order to specify fine grained variance. |
78 | 100 |
|
79 | | -#### MyVariance |
| 101 | +### MyVariance |
80 | 102 |
|
81 | 103 | MyVariance is the most customizable one, and allows you to experiment with different types of variance. With this one there are several parameters you can provide to specify what variance rules should apply: |
82 | 104 |
|
83 | | -| Parameter | Description | Possible values | |
84 | | -| ---------- | ----------------------------------------- | ----------------------------------- | |
85 | | -| `variance` | Specifies which variance to use | COVARIANT, CONTRAVARIANT, INVARIANT | |
86 | | -| `depth` | How deep subtyping goes | Integer value ≥ 0 | |
87 | | -| `strict` | Whether strict checks should be performed | `true`, `false` | |
| 105 | +INVARIANT, |
| 106 | +COVARIANT, |
| 107 | +CONTRAVARIANT, |
| 108 | +BIVARIANT, |
| 109 | +SIDEVARIANT |
| 110 | + |
| 111 | +| Parameter | Description | Possible values | |
| 112 | +| ---------- | ------------------------------- | ----------------------------------------------------------- | |
| 113 | +| `variance` | Specifies which variance to use | COVARIANT, CONTRAVARIANT, INVARIANT, BIVARIANT, SIDEVARIANT | |
| 114 | +| `depth` | How deep subtyping goes | Integer value ≥ 0 | |
| 115 | +| `strict` | Whether | `true`, `false` | |
88 | 116 |
|
89 | 117 | #### Covariant |
90 | 118 |
|
91 | 119 | Covariant is a specific instance of MyVariance. It's intended to inline with the semantics of traditional covariance. It acts as MyVariance with `variance` set to `COVARIANT`, `depth` as infinite and `strict` to `true`. |
92 | 120 |
|
| 121 | +<details> |
| 122 | + <summary> |
| 123 | + Code example |
| 124 | + </summary> |
| 125 | + |
| 126 | +```java |
| 127 | +public class ImmutableList<@Covariant T> { |
| 128 | +List<T> underlyingList = new ArrayList<>(); |
| 129 | + |
| 130 | + public ImmutableList(Iterable<T> initial) { |
| 131 | + initial.forEach(e -> underlyingList.add(e)); |
| 132 | + } |
| 133 | + |
| 134 | + public int size() { |
| 135 | + return underlyingList.size(); |
| 136 | + } |
| 137 | + |
| 138 | + public boolean isEmpty() { |
| 139 | + return size() == 0; |
| 140 | + } |
| 141 | + |
| 142 | + public boolean contains(Object o) { |
| 143 | + return underlyingList.contains(o); |
| 144 | + } |
| 145 | + |
| 146 | + public Iterator<T> iterator() { |
| 147 | + return underlyingList.iterator(); |
| 148 | + } |
| 149 | + |
| 150 | + public Object[] toArray() { |
| 151 | + return underlyingList.toArray(); |
| 152 | + } |
| 153 | + |
| 154 | + public T get(int i) { |
| 155 | + return underlyingList.get(i); |
| 156 | + } |
| 157 | + |
| 158 | +} |
| 159 | +``` |
| 160 | + |
| 161 | +</details> |
| 162 | + |
93 | 163 | #### Contravariant |
94 | 164 |
|
95 | 165 | Contravariant, similarly to Covariant, aims to inline with the semantics of traditional contravariance. It acts as MyVariance with `variance` set to `CONTRAVARIANT`, `depth` as infinite and `strict` to `true`. |
96 | 166 |
|
97 | | -### Subsection |
| 167 | +<details> |
| 168 | + <summary> |
| 169 | + Code example |
| 170 | + </summary> |
| 171 | + |
| 172 | +```java |
| 173 | +class Pair<@Contravariant X, @Contravariant Y> { |
| 174 | + private X first; |
| 175 | + private Y second; |
| 176 | + |
| 177 | + public Pair(X first, Y second) { |
| 178 | + this.first = first; |
| 179 | + this.second = second; |
| 180 | + } |
| 181 | + |
| 182 | + public void setFirstElement(X first) { |
| 183 | + this.first = first; |
| 184 | + } |
| 185 | + |
| 186 | + public Y setSecondElement(Y second) { |
| 187 | + this.second = second; |
| 188 | + } |
| 189 | +} |
| 190 | +``` |
| 191 | + |
| 192 | +</details> |
98 | 193 |
|
99 | 194 | ## Contributing |
100 | 195 |
|
|
0 commit comments