Live data from Hacker News

JEP draft: Prepare to make final mean final

openjdk.org

21–30 of 143 posts

Re: JEP draft: Prepare to make final mean final

#21
Hmm, not a bad approach.

I think the one thing that'd be nice is if I could somehow tell the JVM from a class that this class is open for final mutation rather than needing special flags passed into the JVM or special manifests in the Jar. It's often pretty clear to me, as a dev, what I when I need something to have final mutation (generally only with serialization objects).

For example,

    @FinalMutatableByReflection
    class Foo {
      final String bar;
    }
That'd allow me to transition code by just adding an annotation where it needs to be while also getting the benefit that final is really final everywhere else in code that isn't working with serialization.

Re: JEP draft: Prepare to make final mean final

#22
post #11

Earlier quoted context omitted.

Const-ness in C++ is something I miss in other languages. Being immediate able to see that this function or method couldn't mutate the object made it so much easier to reason about the code. Yeah I know there's ways around it, but then the author known what they told the other party to expect.

Yeah I find it a bit startling going from rust (where const is the default) to basically any other language. Sometimes I look at typescript function definitions and I’m like - uuuuhhh does this function mutate that parameter? Does it keep a reference to it? If the object I’m passing is mutated after I call this function, will something break? It’s impossible to tell from a function signature, even with all of typescr…

> Even the JS standard library struggles with this. ...

I don't think this represents struggling. There needs to be some way to sort in place. Sometimes you need to sort a big array and don't want to allocate.

And there should be some way to clone the array without mutating. That's slice. So you how do you sort a clone? .slice().sort()

I think by far the biggest problem with ES .sort() is that number arrays don't sort numerically by default.

Re: JEP draft: Prepare to make final mean final

#23

Great! Now can we make `final` the default for all fields, variables, and parameters? (yes yes, I know, that would break syntax... but please come up with something to discourage mutability)

  What man that sees the ever-whirling wheel
  Of Change, the which all mortal things doth sway,
  But that thereby doth find, and plainly feel,
  How Mutability in them doth play
  Her cruel sports to many men's decay?
(Edmund Spenser, 1596)

Re: JEP draft: Prepare to make final mean final

#24
post #17
post #4

From my perspective as a C++ developer, every attempt to use `const` for compiler optimization appears to be stymied by the existence of `const_cast`, because modifying a `const` value is only undefined behaviour if the underlying object is `const`. Glad to see that Java is willing to break the language to improve it.

> modifying a `const` value is only undefined behaviour if the underlying object is `const`. I found this sentence confusing. You mean that modifying a value that has been const_cast is undefined behaviour only if the original variable was const right? Or something else?

(Rereading your comment, it sounds like you might already know all of this. Apologies.)

If I understand correctly, here's a C++ example that has undefined behavior:

    int foo() {
        const int x = 42;
        int *p = (int *)&x;
        *p += 1;
        return x;
    }
foo() is UB, because it modifies the const object x, using a pointer cast to "cast away const". (Unfortunately, UBSan doesn't catch this, and I'm not aware of any sanitizer that does.) It's tempting to say that the pointer cast is "at fault" for the UB, but consider this very similar example that's not UB:

    int bar() {
        int x = 42;
        const int *const_p = &x;
        int *p = (int *)&const_p;
        *p += 1;
        return x;
    }
bar() has exactly the same pointer cast as foo(), however in this case the original x object is not const. That makes "casting away const" legal in this case. So the problem we're left with, is that knowing all the types isn't enough for us to tell whether this cast is going to cause UB. We have to know where the pointer originally came from, which might be in another function or another file.

Re: JEP draft: Prepare to make final mean final

#25

Great! Now can we make `final` the default for all fields, variables, and parameters? (yes yes, I know, that would break syntax... but please come up with something to discourage mutability)

Const-ness in C++ is something I miss in other languages. Being immediate able to see that this function or method couldn't mutate the object made it so much easier to reason about the code. Yeah I know there's ways around it, but then the author known what they told the other party to expect.

If possible, 1) design completely immutable data structures that can be broadly shared and don't need to be copied. If you need mutability, just embrace the fact that someone is going to abuse mutability and 2) try to create abstractions that can suffer abuse. If you're coming from a language that doesn't have const, you learn to build things that are hard(er) to screw up.

While bad code can exist in any language, I get worried about too much const in code, because it means they failed at both 1) and 2) and instead there are usually seriously tricky protocols that must be observed to make the thing work. I often ran into code where people were sprinkling const all over the code to lock things down but they fundamentally did not understand the design and made it nearly impossible to evolve, unless you used casts to get rid of const, which defeats the whole purpose.

I'm not saying const doesn't have value, but it's weapon #3, not weapon #1.

Re: JEP draft: Prepare to make final mean final

#26

Earlier quoted context omitted.

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 sen…

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 const…

> 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.

Re: JEP draft: Prepare to make final mean final

#27
post #8

I don’t see a way to opt-in to a hard error if this happens somewhere in the guts of the code (third-party probably).

> --illegal-final-final-mutation=deny will result in Field::set throwing an IllegalAccessException for every illegal final field mutation.

Seems like this way?

Re: JEP draft: Prepare to make final mean final

#28
post #11

Earlier quoted context omitted.

Yeah I find it a bit startling going from rust (where const is the default) to basically any other language. Sometimes I look at typescript function definitions and I’m like - uuuuhhh does this function mutate that parameter? Does it keep a reference to it? If the object I’m passing is mutated after I call this function, will something break? It’s impossible to tell from a function signature, even with all of typescr…

> Even the JS standard library struggles with this. ... I don't think this represents struggling. There needs to be some way to sort in place. Sometimes you need to sort a big array and don't want to allocate. And there should be some way to clone the array without mutating. That's slice. So you how do you sort a clone? .slice().sort() I think by far the biggest problem with ES .sort() is that number arrays don't sor…

> There needs to be some way to sort in place. Sometimes you need to sort a big array and don't want to allocate

Your parent isn't saying there shouldn't be mutation, just that the mutation should be obvious.

In Rust, the type signature for the in-place sort is

  pub fn sort(&mut self)
  where
      T: Ord,
That `&mut self` lets you know that it's going to mutate.

Re: JEP draft: Prepare to make final mean final

#29

Hmm, not a bad approach. I think the one thing that'd be nice is if I could somehow tell the JVM from a class that this class is open for final mutation rather than needing special flags passed into the JVM or special manifests in the Jar. It's often pretty clear to me, as a dev, what I when I need something to have final mutation (generally only with serialization objects). For example, @FinalMutatableByReflection c…

[deleted]

Re: JEP draft: Prepare to make final mean final

#30

    [Speculative optimizations] may not suffice in this case as future planned optimizations may wish to rely not only on immutability within the lifetime of the process, but also on the immutability of fields from one run of the application to the next.
Can someone elaborate a little more on what this means? I'm very surprised to hear that this was considered a blocker important enough to add all of this complicated machinery (and breaking several deserialization libraries...), when I've never even heard of such an optimization and can't imagine what sort of form it would take
Post reply on HN