Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

All the while, adding its own mistakes to the pile.

As an example, idiomatic Java style is moving away from getters/setters and instead favors builders and immutable types. That whole inheritance vs. composition concept is changing the way Java is being used.

Meanwhile, C# has baked getters/setters so deep into their language (as properties) that there's just no moving off of them. Have you tried making a Builder in C# - ugh. Much harder than it should be.

C# took the best of Java and ran deep into a cave with it. Meanwhile, Joshua Bloch's Effective Java has completely changed the way that "good" Java should be written and likely helped kickstart the whole revolution that is modern Java.

I agree with your sentiment in general. C# / CLR are truly remarkable engineering feats. But Java is starting to break away from its old molds and is seemingly adding agility as it goes.

As far as ownership. Java is not owned by Oracle any more than C# is not owned by Microsoft. They are both open languages with a majority backing supporter that yes, has a lot of influence over the languages. I'd argue Microsoft has more influence over C# / CLR than Oracle does on Java / JVM (but that's too close to call and probably too opinionated to suggest).



Yes. And with "records" (record classes, something with the characteristics of a tuple or struct) you can get further away from getter/setter boilerplate code.


I just wish records had an easy way to facilitate and/or associate a builder with them. Wishful thinking, out of scope for what they are. And of course, it's not hard to write a FooBuilder that's defined to help construct a Foo record.

If Java records could be told to have a private constructor, I'd be completely satisfied with them. I just don't like the ability for callers to be able to directly instantiate a record without having gone through my builder to do so. I want to completely enforce that my record is instantiated with all its invariants dealt with properly. A builder is a very nice way of doing that.


I wanted to experiment with creating a "Rust-like" option and result type for Java and so figured that I would need records and pattern matching for this and I ran into exactly what you are talking about here with records and public constructors.

My solution was to create a sealed interface that permitted the None and Some records as the only classes to implement it. Those records are not available outside the package, while the interface is exposed. Using default methods in the interface I could expose a state "create()" method which would then instantiate the appropriate None or Some record. In this way you control the exposure of the construction of the specific record implementations of your interface.

You can then either interact with the option through the methods on the interface, .isOk(), .unwrap(), etc, etc, or with the upgraded pattern matching in switches with this release you could have something like

  switch(option) {
    case option when option.isNone() -> blah
    case option when option.isSome() -> foo
}

Its not as pleasing as Rust matching directly on Some and None, but it gets you pretty close.


This is not really pattern matching, that’s just a regular old if-else.

https://news.ycombinator.com/item?id=35133670

This is absolutely possible in Java and the only less-than-ideal part is the generic type having to be specified in the None case (but a trivial method fixes that as well)

This can be used just like rust and similar languages:

  switch (option) {
    case Some(var x) -> println(x);
    case None -> // TODO
  }
Hell, you can just further pattern match inside Some, like `Some(Point(var x, var y))`


Java is built upon it's community :), look no further: https://github.com/Randgalt/record-builder, although hiding the constructor behind the builder is not something I am sure supported by that library

I also remember a discussion in the mailing list about withers: https://mail.openjdk.org/pipermail/amber-spec-experts/2020-M...

Not sure where it stands now, you can ask in the mailing list about this


> I want to completely enforce that my record is instantiated with all its invariants dealt with properly

Such validation logic can be enforced in the record's compact constructor.


Yes, agreed. But not in a clean "fluent" style. There's something nice about the fluent style that appeals to me (at least).

And I don't like having to provide one constructor for every optional (default) value, when its omitted. There's just not as nice of a style. The "telescoping" constructor pattern is just really hard to use, read and maintain.


Aren't records and init only setters in C# also a way to favor immutable types and fix setters in properties? (Not trying to criticize, I really want to know).


Kind of. It gets you close.

With the Builder pattern, you can easily define complex relationships for your class inner state. Like maybe if A & B are specified, then C shouldn't be specified. But if C is specified, then D should have a default value. Etc.

These types of initialization requirements are not easily duplicated with C# init-only construction. Instead, in C# you have to rely on the callers to know exactly how to correctly instantiate your class, including all the complex logic like in the example above.

So yes, C# init-only setters are kind of the best option you have. You can still create a C# FooBuilder for a Foo class. But generally C# programmers want to go out of their way to directly instantiate a Foo, and the number of bugs you get from this is crazy. It's a culture thing, honestly, not as much a language problem.

My two cents.


Do you have an example of how builders work in Java?


This is probably a pretty good link to start from.

https://blogs.oracle.com/javamagazine/post/exploring-joshua-...




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: