Valhalla breaks minimal-j framework
Ethan McCue
ethan at mccue.dev
Mon Dec 1 21:00:43 UTC 2025
On that note, i'd say you have somewhat of a bigger problem w.r.t. enums
@SuppressWarnings({ "rawtypes", "unchecked", "restriction" })
public static <T extends Enum<T>> T createEnum(Class<T> clazz, String name) {
try {
Constructor constructorToCall =
Enum.class.getDeclaredConstructor(String.class, Integer.TYPE);
sun.reflect.ReflectionFactory f =
sun.reflect.ReflectionFactory.getReflectionFactory();
Constructor c = f.newConstructorForSerialization(clazz, constructorToCall);
T e = (T) c.newInstance(name, Integer.MAX_VALUE);
return e;
} catch (Exception x) {
throw new RuntimeException(x);
As with all things in jdk.unsupported, I can't help but wonder if
there are any VM level invariants you run afoul of by making extra
enum instances.
My first stab at #2 is to add something like this
record Field<T>(T value) {}
And start making value types require wrapping. Or instead of providing
your own type special case the idiom of "record with single component
for adding identity to a field that otherwise wouldn't have it." Or go
a little crazy and make a Field that distinguishes between when its
being used as an actual value holder or as a unique sentinel.
public sealed abstract class Field<T> {
public abstract T get();
public abstract void set(T value);
public static <T> Field<T> withInitialValue(T value) {
return new Field.Actual<>(value);
}
// Maybe encode the path?
public static <T> Field<T> reference(String name) {
return new Ref<>(name);
}
private static final class Actual<T> extends Field<T> {
private T value;
Actual(T value) {
this.value = value;
}
@Override
public T get() {
return value;
}
@Override
public void set(T value) {
this.value = value;
}
@Override
public String toString() {
return "Field[" + value + "]";
}
}
private static final class Ref<T> extends Field<T> {
private String name;
Ref(String name) {
this.name = name;
}
@Override
public T get() {
throw new IllegalStateException("Cannot get a ref");
}
@Override
public void set(T value) {
throw new IllegalStateException("Cannot set a ref");
}
@Override
public String toString() {
return "Field[ref=#" + name + "]";
}
}
}
I think it's also worth asking - do you have any usage statistics on
your library? (Maven central used to offer download stats, etc.) If
you are going to be broken anyways it is probably useful to know the
blast radius.
On Mon, Dec 1, 2025 at 3:36 PM Bruno Eberhard <bruno.eberhard at pop.ch> wrote:
> Am 01.12.2025 um 21:10 schrieb Ethan McCue:
> > I think I'm understanding. So $ has the same type as the source class,
> > but each field on it is initialized to something with a unique identity,
> > their values aren't important, their identity is.
>
> Exactly.
>
> You pretty much got all my ideas around the problem right.
>
> 1) code generation
>
> I simply don't like code generation. It makes setup of projects
> difficult. It easily breaks because of some wrong paths or forgotten
> re-generate. Has to integrate in IDEs.
>
> 2) special field classes
>
> Using the normal classes is so much more natural. No need to convert.
>
> 3) Use values instead of always "new Integer(0)".
>
> Yes this works. For almost all supported classes like: String, Integer,
> Long, LocalDate, LocalDateTime, LocalTime, BigDecimal.
>
> For Boolean and Enum I would have to use the second solution.
>
>
> I started with the framework with java 8. Never thought at that time
> this would become a problem.
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/valhalla-dev/attachments/20251201/0904ceb3/attachment.htm>
More information about the valhalla-dev
mailing list