-1

I have a struct that doesn't enforce any invariants, (i.e. it exists to carry data for other classes), so its fields are public and it has no (declared) constructor:

public class Observation {
    public short truncatedSeed;
    public final int[] levels = { 0, 0, 0 };
    public final int[] stride1 = { -1, -1, -1 };
    public final int[] stride2 = { -1, -1, -1 };
    public int power = -2;
    public long now = -1; // Not used in equals()/hashCode()
    @Nullable public Item item;
}

If I want to create a static final instance of this, how do I do that? Have newer Javas finally introduced good syntax for handling this case? The best I've been able to come up with is:

public static final EMPTY = new Observation() {
    {
        truncatedSeed = (short) 0xDEAD;
        item = Item.EMPTY;
    }
};

...which does work, but creates an anonymous subclass, which seems... icky.

Edit: Since Java's final is shallow, none of the members here will be immutable. Given that, the most straightforward approach is probably to embrace this fact:

public static EMPTY = new Observation();
static {
    truncatedSeed = (short) 0xDEAD;
    item = Item.EMPTY;
}
D0SBoots
  • 705
  • 6
  • 18
  • 3
    Why not using a constructor or builder? – Nowhere Man Jan 05 '21 at 00:04
  • Your code implies you want a constructor but you say the 'structure' doesn't have one - well why not add one then? Java already provides exactly what you need, there isn't any reason for you to create a sub-class. – stridecolossus Jan 05 '21 at 00:09
  • Why do you feel the need to create an anonymous subclass? What's wrong with using `EMPTY.truncatedSeed = (short) 0xDEAD;` after `EMPTY` is initialized? `Observation` is a mutable class. – Charlie Armstrong Jan 05 '21 at 00:09
  • This doesn't make sense. First, do you mean `public static final Observation EMPTY = ...`? Second, what's wrong with a standard Singleton pattern (private constructor with `public static Observation getInstance()`) if you really want a Singleton? Or, if you want multiple instances, a public constructor and `public static Observation getEmptyObservation()` which returns the single saved empty instance? I guess you need to clarify what you _really_ want. – Jim Garrison Jan 05 '21 at 00:11
  • I'm getting the sense from these comments that "Java doesn't work that way!" and I'm out of luck. :) As to why I don't use a constructor or builder (which basically encapsulates the other comments): A constructor would just be a bunch of extra boilerplate for this one thing. "No code is the best code." Using a builder is even worse - the builder would essentially just be a copy of my existing struct. – D0SBoots Jan 05 '21 at 00:13
  • The syntax I would prefer would be something like this: https://en.cppreference.com/w/cpp/language/aggregate_initialization#Designated_initializers – D0SBoots Jan 05 '21 at 00:17
  • @CharlieArmstrong EMPTY can't be modified after it's created if it's final. And the point of it being final is the usual thread-safety, etc. reasons. – D0SBoots Jan 05 '21 at 00:21
  • Sure, I suppose Java doesn't work exactly like C++. It requires more boilerplate because you're doing something different. A C++ struct is a way of grouping data, but a Java object is a way of grouping data and the functions that act on it. The design goals are completely different. EDIT: `EMPTY` is final, but not immutable. Almost everything is a reference in Java. You can't change the reference, but you can change the object it points to. This is called a "mutable" object. – Charlie Armstrong Jan 05 '21 at 00:22
  • Actually @D0SBoots, the _reference_ `EMPTY` cannot be modified, but the object pointed to, with all its public members, ***can*** be modified. And no, I don't think Java works quite the way you want it to, but without more detail it's hard to tell exactly what you're trying to accomplish. If you want an unmodifiable instance you must hide its members behind getters and do not provide any methods that mutate the object's internal state. – Jim Garrison Jan 05 '21 at 00:22
  • it seems you want this object to be a mixture between [DTO and VO](https://stackoverflow.com/questions/1612334/difference-between-dto-vo-pojo-javabeans). In my opinion it should be better to make clearer the immutability of the final properties using getters without setters; I think this could be more close to the Java way – fantaghirocco Jan 05 '21 at 00:38
  • @JimGarrison, CharlieArmstrong Ah yes, Java's shallow final trips me up again. You are right that everything remains mutable, so there is little purpose in having a final-ish constant; mind as well leave off the final to be more clear. – D0SBoots Jan 05 '21 at 02:25
  • 1
    @fantaghirocco Thanks for that link - it was informative, and also serves to reinforce that what I am doing here runs counter to "the Java way" (despite being very reasonable in other languages). – D0SBoots Jan 05 '21 at 02:49

2 Answers2

4

Have newer Javas finally introduced good syntax for handling this case?

Yes. Twice, even.

But, records are by definition immutable. Every field is implicitly final. They also don't support having some of the field have default values, and they don't support builders. Arriving in Java 16.

A constructor would just be a bunch of extra boilerplate for this one thing.

Yup. That's what lombok does. Gets rids of boilerplate. These do get a builder if you want them to, and can have default values for individual fields. @Value defaults to immutability but you can 'unfinalize' a field if you want, and you can also create a type hierarchy; all things records don't do. Also, lombok works since java6, records are preview feature and can't be fully used without enabling them until at least java 16 (March 2021).

The syntax is would prefer would be something like: (from CPP):

struct A { int x; int y; int z; };
A b{.x = 1, .z = 2}; // ok, b.y initialized to 0

That's, effectively, 'named parameters', and java doesn't have that. But it has methods, which also have names. In java, with lombok:

@Builder @Value class A { int x, y, z; }
A a = A.builder().x(1).z(2).build(); // ok, y initialized to 0.
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
rzwitserloot
  • 85,357
  • 5
  • 51
  • 72
  • Records (and the justification for them) sounds like precisely what I was looking for... but they're not here yet (and even if they were I'm stuck at Java 9 I'm pretty sure.) I've come across Lombok before, but it's a "high power but high initial-cost" sort of thing. – D0SBoots Jan 05 '21 at 02:33
  • @D0SBoots Well, as they say, you can lead a horse to water... :P – your attitude about java seems to indicate you will easily get that initial cost paid back in full and then some. Then again, I'm one of lombok's core maintainers so I might be a _tad_ biased. – rzwitserloot Jan 05 '21 at 03:27
  • Haha, fair. In my *specific* case, this is within the context of a Minecraft mod, and the build process there is... exciting. I don't even want to depend on a normal library, let alone something that requires build-time support. But for more usual Java development, lombok makes plenty of sense. – D0SBoots Jan 05 '21 at 05:26
-2

You could create a constructor with those two parameters you want to initialize. (After all, Java will always require a constructor for creating new objects under the hood.)

import lombok.*;

@NoArgsConstructor
@RequiredArgsConstructor
public class Observation {
  ...
}

public static final EMPTY = new Observation(0xDEAD, Item.EMPTY);
Mick
  • 954
  • 7
  • 17
  • 2
    I see two problems with this answer: 1) It uses a constructor, when the OP specified they didn't want a constructor. You should wait to see their reasoning before posting an answer. 2) It doesn't show how to write a constructor, only how to call one. I'm not writing this to be mean, I'm writing it in hopes of it being helpful to you. – Charlie Armstrong Jan 05 '21 at 00:29
  • The OP doesn't literally say "want no constructors", but I see your point. It seems unwanted. I will remove this answer as soon as anyone comes up with a solution to what I guess is unresolvable. Static declarations are just too restrictive. – Mick Jan 05 '21 at 00:49