Most active commenters

    ←back to thread

    207 points mfiguiere | 11 comments | | HN request time: 1.75s | source | bottom
    Show context
    bironran ◴[] No.43539321[source]
    A cursory glance at "setAccessible" usage reveals popular libraries such as serializers like gson and jaxb, class manipulation and generation like cglib, aspectj and even jdk.internal.reflect, testing frameworks and libraries including junit, mockito and other mocking libraries, lombok, groovy, spring, and the list goes on and on.

    My bet is that this will be yet another "checked exception" or "module system", where many applications now need to add "--add-opens". If you'll use ANY of many of the more popular frameworks or libraries you'll end up giving this assurance away, which will make library developers not able to rely on it and we're back to square one.

    replies(4): >>43539649 #>>43539702 #>>43540421 #>>43540453 #
    1. PathOfEclipse ◴[] No.43539649[source]
    setAccessible is also used to be able to access private fields, and not just to be able to write to final fields. Most libraries shouldn't need to set final fields, and I say this as someone who was very against when they deprecated java.lang.misc.Unsafe. I've only had to set a final field once in my career and it was related to some obscure MySql/JDBC driver bug/workaround. This particular deprecation seems very sensible to me.
    replies(1): >>43539750 #
    2. eastbound ◴[] No.43539750[source]
    So how should GSON initialize an object?

    The theory is, go through the constructor. However, some objects are designed to go through several steps before reaching the desired state.

    If GSON must deserialize {…, state:”CONFIRMED”}, it needs to call new Transaction(account1, account2, amount), then .setState(STARTED) then .setState(PENDING) then .setState(PAID) then .setState(CONFIRMED) ? That’s the theory of the constructor and mutation methods guarding the state, so that it is physically impossible to reach a wrong state.

    There is a convention that deserialization is an exception to this theory: It should be able to restore the object as-is, after for example a transfer over the wire. So it was conventionally enabled to set final variables of the object, but only at initialization and only for its own good. It was assumed that, even though GSON could reach a state that was unachievable through normal means, it was, after all, the role of the programmer to add the right annotations to avoid this.

    So how do we do it now?

    replies(4): >>43539839 #>>43540043 #>>43541812 #>>43543379 #
    3. vips7L ◴[] No.43539839[source]
    Why would you use GSON for objects that go through steps of state? Why would you mark fields like State as final when it is actually mutable? This just sounds like poorly designed code.

    Maybe I don't know of your use case, but GSON/Jackson/Json type classes are strictly data that should only represent the data coming over the wire. If you need to further manipulate that data it sounds like the classes have too much responsibility.

    replies(1): >>43540238 #
    4. steveklabnik ◴[] No.43540043[source]
    > So how do we do it now?

    The JEP says:

    > the developers of serialization libraries should serialize and deserialize objects using the sun.reflect.ReflectionFactory class, which is supported for this purpose. Its deserialization methods can mutate final fields even if called from code in modules that are not enabled for final field mutation.

    I don't know enough about the details here to say if that's sufficient, but I imagine that it at least should be, or if it's not, it will be improved to the point where it can be.

    replies(1): >>43541216 #
    5. bdangubic ◴[] No.43540238{3}[source]
    all state is immutable :) a change creates new state - which is immutable
    replies(1): >>43540245 #
    6. vips7L ◴[] No.43540245{4}[source]
    :) no its not.
    replies(1): >>43541215 #
    7. cesarb ◴[] No.43541216{3}[source]
    > The JEP says: [...]

    The JEP also says:

    > The sun.reflect.ReflectionFactory class only supports deserialization of objects whose classes implement java.io.Serializable.

    In my experience, most classes being deserialized by libraries like GSON do not implement Serializable. Implementing Serializable is mostly done by classes which want to be serialized and deserialized through Java's native serialization format (which is used by nothing outside Java, unlike cross-platform formats like JSON or CBOR).

    8. bdangubic ◴[] No.43541215{5}[source]
    if you change the state, it is not same state, it is a new state
    9. twic ◴[] No.43541812[source]
    It strikes me that we could have a way to reflectively create an object from values for all its fields in a single step - similar to what record constructor does, but for any class (could even be Class::getCanonicalConstructor, returning a java.lang.reflect.Constructor). It would be equivalent to creating an uninitialised instance and then setting its fields one by one, but the partly-initialised object would never be visible. This should probably be restricted, because it bypasses any invariants the constructor enforces, but as you say, ultimately serialisation libraries do need to do that.
    replies(1): >>43541902 #
    10. recursivecaveat ◴[] No.43541902{3}[source]
    I don't know if Java serialization supports this kind of thing, but if you have object A has a pointer to object B and vice-versa, there's no order to deserialize them without passing through a partially-initialized state that preserves the object identity relationship. I suppose you can't construct this kind of loopy references graph with final fields without reflection in the first place, so it's kindof chicken and egg. For the very common case of DAG-shaped data or formats that don't support references I think the one-shot internal constructor works though.
    11. n_plus_1_acc ◴[] No.43543379[source]
    Just do it like Rust