Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Somebody asked this two years ago, but it's archived now: https://www.reddit.com/r/java/comments/yfdofb/anyone_tested_jdk_20_early_access_build_for/
- For my tests I created a primitive version of a relatively simple data structure I once created for a sudoku solver (it was a project at uni):
- https://github.com/claudemartin/smallset/tree/valhalla
- It's a bit field that uses all 32 bits of an int. That means it can hold the values 0 to 31 (inclusive). "SmallSet" isn't a great name, but it is a set and it is "small" because it is limited to only 32 bits.
- Here are my opinions:
- It's relatively easy to use. You really can just use the new keyword "primitive" to make any class primitive.
- It is stable. I tried the same with Java 14 Valhalla and back then it crashed when I let it run the unit tests in a loop. But now I didn't experience any such problems except for serialisation.
- Since Eclipse doesn't support Valhalla I used ANT and a very simple batch script (I'm on Windows 11). Getting it to run on another system should be just as easy.
- It's weird that you have to use new Foo() to create a primitive value (not a reference). We are used to using the "new" keyword to create a new reference, which means memory is allocated on the heap. But now "new" just means you call a constructor.
- You get an additional type for a boxed version. If you create a primitive class "Foo", you also get "Foo.ref". Autoboxing works fine. We might even get int.ref as an alias for java.lang.Integer, but that's not the case yet.
- Var-args and overloads can be tricky. If you have myMethod(Object... values) and you call it using your own primitive type "Foo", you get an Object[] containing only boxed values. You can also get a situation where you don't call the method you want when there are overloads and the compiler uses autoboxing. However, when I created myMethod(SmallSet... values)it didn't compile, because the compiler thinks it's ambiguous. But isn't the second one more specific? Same if you have m(Foo...) and m(Foo.ref[]). And often you have legacy code that has overloads for the existing primitives and everything else goes to a methods that accepts"Object" or "Object[]". That still works in most cases but even if they don't allow overloads with arrays of value types, there will probably be some issues. You can still use getComponentType to check the type. But array.getClass().getComponentType().isPrimitive() will return false. You must use isValue / isIdentity instead.
- Reflection is now a lot more complex and lots of frameworks will not work. So they added isValue and they also added Modifier.VALUE. But we use the keyword "primitive", not "value". This is extremely confusing. You create a primitive class and it's not primitive?! The modifier "primitive" is actually called "value" in reflection?! But then there's also "PrimitiveClass.PRIMITIVE_CLASS" and now I'm just confused. And isValue is true even if you use it on a Foo.ref type, which is auto-generated and used whenever a reference is required. But how would you know whether a Class<?> is the primitive type or a boxed version of it? There's isPrimitiveValueType, which isn't public.
- And I found more issues with arrays. It's ok that you cant use null inside a SmallSet[]. But somehow I can assign a SmallSet[] to an Object[]. It's not new that you can completely break type safety in Java by assigning some array to some variable with an array type that has a more general component type. But the values inside that Array are actually values. Right now Java can't convert from int[] to Object[], but with Valhalla it can convert from SmallSet[] to Object[]. That makes no sense. But if this is really so it would explain the problem I had with the overloads.
- We still need support for generic types, such as Stream, Optional, Comsumer, etc. It's great that primitives can't be null, but when you want to use Optional you'd have to use the boxed version. There is OptionalInt for integers, but there wouldn't be an Optional for your custom primitive, even if it only uses an int, like my SmallSet. Since we don't even have ByteStream or FloatStream, we might not get a Stream for any custom primitive type. The constant autoboxing will diminish the benefits of suing primitive types. This might come in a different release if they ever actually implement JEP 218.
- Serialisation does not work at all. You can't write it to an ObjectOutputStream because there is no writePrimitive that would accept any custom value type. I created a simple record to hold the primitive value and it doesn't work. You can run the unit tests to reproduce the problem. It might be necessary to implement writeObject() and readObject() so that our custom primitives can be serialised. But I hope this will be fixed.
- It is faster. More than twice as fast on my system and with my simple test. I created thousands of such "small sets" to add and remove random numbers and create the complement. On my machine this is about twice as fast. This isn't on the repo but all I had to do is copy the primitive class to a different package and remove the "primitive" and some of the methods that wouldn't compile. I used System.nanoTime() and I measured after a few warm up iteration. It was less than 50s vs more than 100s. I didn't measure memory usage as this would require better benchmarking.
- After all that I still hope we soon get something similar to what we already have in this preview.
- Serialisation has to be fixed as some frameworks use it and reflection could be a bit simpler. Arrays shouldn't be used in APIs anyway. The performance is actually much better and so it would be worth it. And I'm sure a lot of other languages that can run on the JVM, such as EcmaScript, Python, and Ruby, will also benefit from this. And IDEs will probably have lots of helpful tips on how to prevent autoboxing.
Advertisement