You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+46-9Lines changed: 46 additions & 9 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,9 +17,13 @@ This project provides Java annotations that enable fine-grained specification of
17
17
-[Background](#background)
18
18
-[What is variance](#what-is-variance)
19
19
-[Types of variance](#types-of-variance)
20
+
-[New variance notions introduced in this project](#new-variance-notions-introduced-in-this-project)
21
+
-[Depth](#depth)
22
+
-[Side Variance](#side-variance)
23
+
-[Specifying subtyping relationships with required types](#specifying-subtyping-relationships-with-required-types)
20
24
-[Usage](#usage)
21
25
-[Annotations](#annotations)
22
-
-[Output](#output)
26
+
-[Building and running your project](#building-and-running-your-project)
23
27
-[Contributing](#contributing)
24
28
25
29
## Installation
@@ -113,7 +117,7 @@ If we define a depth of 2:
113
117
114
118
For example, consider the following type hierarchy:
115
119
116
-
`Animal`├── `Mammal`├── `Dog`
120
+
`Animal` ── `Mammal` ── `Dog`
117
121
118
122
If we define a depth of 1:
119
123
@@ -128,11 +132,42 @@ For example, onsider the types `Animal`, `Dog`, and `Cat`, where `Dog` and `Cat`
128
132
-`List<Dog>` would be a subtype of `List<Cat>`.
129
133
- Similarly, `List<Cat>` would be a subtype of `List<Dog>`.
130
134
135
+
#### Specifying subtyping relationships with required types
136
+
137
+
By specifying a list of type names, you can enforce that any subtype must relate to all of the provided types. Here, the concept of "relate" means that the subtype must either be a **subtype** or a **supertype** of the specified types.
138
+
139
+
**Required Subtypes**
140
+
You can supply a list of required subtypes, meaning all types in the list must be subtypes of the given type for it to qualify as a valid subtype.
141
+
142
+
**Required Supertypes**
143
+
Conversely, for required supertypes, the given type must be a supertype of all the specified types to be considered valid.
144
+
145
+
This allows for control over the branching points in your type hierarchy.
146
+
147
+
To illustrate with an example, consider the following type hierarchy:
148
+
149
+
`Animal` ── `Mammal` ── `Dog`
150
+
151
+
└── `Bird` ── `Parrot`
152
+
153
+
If you specify a list to be **covariant** with the required subtypes as `[Dog]`, then:
154
+
155
+
-**`List<Mammal>`** would be a valid subtype of **`List<Animal>`**, because `Dog` is a subtype of `Mammal`.
156
+
-**`List<Bird>`**, however, would **not** be a valid subtype of **`List<Animal>`**, since `Dog` is not a subtype of `Bird`.
157
+
131
158
## Usage
132
159
133
-
To specify variance for classes, annotate the relevant type parameters with one of the provided annotations (details on these annotations will follow). Once annotated, you can use your classes as though they conform to the specified variance. Note that your IDE's linter may flag errors if the classes are used in ways that Java does not natively support. These warnings are expected and can be safely ignored.
160
+
### How it works
161
+
162
+
To specify variance for your classes, annotate the relevant type parameters with one of the provided annotations (details on these annotations will follow). After annotating, you can use your classes as if they conform to the specified variance. However, note that your IDE’s linter may flag errors when using these classes in ways that Java does not natively support. These warnings are expected and can be safely ignored.
163
+
164
+
The reason these warnings can be ignored lies in how Java's generics work. Generics in Java are essentially syntactic sugar-they are erased at compile-time. The compiler erases all generic types and replaces them with type casts to ensure type safety. This same technique is applied here.
165
+
166
+
When projects using these annotations are compiled, an annotation processor runs. This processor analyzes the source files, parsing them into Abstract Syntax Trees (ASTs), and erases any instances of variant class arguments by replacing them with their leftmost bound. Later, when the argument is output, the processor inserts a cast back to the type it was originally marked as. This allows us to avoid type-related errors because every instance of the class will be of the exact same type.
167
+
168
+
While this method may seem risky, as it removes type safety and could potentially lead to crashes, the annotation processor ensures type safety is preserved. The processor checks the ASTs to make sure that the types are correctly validated and do not cause issues during runtime.
134
169
135
-
When you compile the project, a new output directory named `output_javavariance`will be created. In this directory, type arguments are erased, and the necessary casts are inserted to ensure the project runs correctly. At this stage, any previously flagged errors should no longer appear.
170
+
When you compile your project, a new output directory named `output_javavariance`is created. In this directory, type arguments are erased, and the necessary casts are inserted to ensure the project runs correctly. At this stage, any previously flagged errors should no longer appear, and the project should function as expected.
136
171
137
172
### Annotations
138
173
@@ -142,11 +177,13 @@ There are currently three annotations provided by this project: `MyVariance`, `C
142
177
143
178
MyVariance is the most customizable one, and allows you to experiment with different types of variance. There are several parameters you can provide to specify what variance rules should apply:
0 commit comments