Skip to content

Commit 2fd4b0c

Browse files
committed
Javadocs on type pattern matching
1 parent e7043b0 commit 2fd4b0c

File tree

4 files changed

+44
-11
lines changed

4 files changed

+44
-11
lines changed

boson/src/main/java/works/bosk/boson/mapping/TypeScanner.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@
7676
* the directive's {@link Directive#spec spec function} will provide a specification
7777
* that is further {@link JsonValueSpec#specialize specialized} to handle the actual type.
7878
* <p>
79+
* In other words, the specification of which types to handle uses
80+
* Java type expressions, interpreted according to {@link DataType#isBindableFrom},
81+
* making it relatively intuitive.
82+
* You can use {@code String} to match exactly that type,
83+
* or you can define a type variable like {@code T extends CharSequence}
84+
* and use that.
85+
* <p>
7986
* The scan aggressively follows any types referenced in {@link TypeRefNode}s,
8087
* so the specifications returned from directives can use those freely,
8188
* rather than needing to recursively specify every referenced type.

boson/src/main/java/works/bosk/boson/types/DataType.java

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ static DataType of(TypeReference<?> ref) {
9393
* handle generics. Note, in particular, that it returns {@code false}
9494
* for boxing and unboxing conversions, as does {@link Class#isAssignableFrom}.
9595
* <p>
96-
* Note that this is neither weaker nor stronger than {@link #isBindableFrom}.
97-
* Type variables will only accept themselves or other type variables
96+
* Note that this is neither weaker nor stronger than {@link #isBindableFrom}:
97+
* type variables will only accept themselves or other type variables
9898
* that are nominally subtypes of them,
9999
* but concrete types can be assigned from subtypes.
100100
*/
@@ -103,12 +103,29 @@ default boolean isAssignableFrom(DataType other) {
103103
}
104104

105105
/**
106-
* {@code A.isBindableFrom(B)} if a value of type List<B>
107-
* can be passed to a method expecting List<A>.
106+
* {@code A.isBindableFrom(B)} if there exists a set of <em>bindings</em>
107+
* that would make {@code A} equal to {@code B}.
108+
* Type variables are bound by their name, so two type variables
109+
* with the same name must be bound to the same type;
110+
* wildcards can be bound to any type that conforms to their bounds.
108111
* <p>
109-
* Note that this is neither weaker nor stronger than {@link #isAssignableFrom(DataType)}.
110-
* Type variables will accept types that conform to their bounds,
111-
* but concrete types cannot be assigned from subtypes.
112+
* This "bindable" concept does not correspond to any operation
113+
* in the Java language, but it is powerful and useful for describing
114+
* sets of related types.
115+
* For example, a type {@code List<Set<?>>} is considered bindable from
116+
* {@code List<Set<String>>}, but Java does not consider them
117+
* to match because {@code List<Set<?>>} describes a list of individually homogeneous sets
118+
* that might differ from each other,
119+
* so {@code list.add(Set.of(1,2,3))} would be allowed
120+
* even though this is not a set of {@code String}s.
121+
* Similarly, {@code List<Set<T>>} where {@code T extends CharSequence}
122+
* is bindable from {@code List<Set<String>>},
123+
* but Java does not consider them to match simply because {@code T}
124+
* represents some specific subtype of {@code CharSequence} that might not be {@code String}.
125+
* <p>
126+
* Note that this is neither weaker nor stronger than {@link #isAssignableFrom(DataType)}:
127+
* type variables will accept types that conform to their bounds,
128+
* but concrete types are invariant and don't match subtypes.
112129
*/
113130
default boolean isBindableFrom(DataType other) {
114131
return isBindableFrom(other, IS_BINDABLE, new HashMap<>());
@@ -121,12 +138,12 @@ default boolean isBindableFrom(DataType other) {
121138
* @param freeVariables if false, unbound type variables match only themselves
122139
*/
123140
record BindableOptions(boolean allowSubtypes, boolean freeVariables){
124-
public static final BindableOptions IS_BINDABLE = new BindableOptions(false, true);
125-
public static final BindableOptions IS_ASSIGNABLE = new BindableOptions(true, false);
141+
static final BindableOptions IS_ASSIGNABLE = new BindableOptions(true, false);
142+
static final BindableOptions IS_BINDABLE = new BindableOptions(false, true);
126143

127144
/**
128145
* Often, whether subtypes are allowed varies during the matching process.
129-
* This lets us assert a new value for that flag while keeping the other the same.
146+
* This lets us assert a new value for that flag while keeping the rest unchanged.
130147
*/
131148
public BindableOptions withAllowSubtypes(boolean v) {
132149
return new BindableOptions(v, this.freeVariables);

boson/src/main/java/works/bosk/boson/types/TypeVariable.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313
* <p>
1414
* When processing the bounds, you can always convert them to {@link DataType}s
1515
* as needed, but be careful to avoid infinite recursion.
16+
* <p>
17+
* TODO: Consider whether {@link #equals} is right here.
18+
* Should two type variables declared in two different places,
19+
* but with the same name and bounds, be considered equal?
1620
*/
1721
public record TypeVariable(String name, List<Type> bounds) implements UnknownType {
1822

boson/src/test/java/works/bosk/boson/types/DataTypeAssignableTest.java renamed to boson/src/test/java/works/bosk/boson/types/DataTypeConformanceTest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
import static works.bosk.boson.types.DataType.INT;
1414
import static works.bosk.boson.types.DataType.OBJECT;
1515

16-
public class DataTypeAssignableTest {
16+
/**
17+
* Tests {@link DataType#isAssignableFrom} and {@link DataType#isBindableFrom}.
18+
*/
19+
public class DataTypeConformanceTest {
1720

1821
@Test
1922
void simpleSubtype() {
@@ -27,6 +30,8 @@ void simpleSubtype() {
2730
void boxing() {
2831
// Boxing/unboxing are allowed by assignment conversion (JLS §5.1.7 / §5.2)
2932
// but Class.isAssignableFrom returns false for these, so who are we to argue?
33+
// Besides, this is useful when we use them in Directives, since typically
34+
// code dealing with primitives doesn't want to deal with boxed types.
3035
assertFalse(INT.isAssignableFrom(Integer.class));
3136
assertFalse(DataType.of(Integer.class).isAssignableFrom(INT));
3237
}

0 commit comments

Comments
 (0)