Skip to content

Conversation

@phax
Copy link
Owner

@phax phax commented Feb 3, 2026

Based on #126

joelittlejohn and others added 7 commits February 1, 2026 10:04
This commit adds:

- EClassType.RECORD for record type declarations
- JRecordComponent class to represent record components
- _record() methods in IJClassContainer to create records
- recordComponent() and recordComponentVararg() methods in JDefinedClass
- compactConstructor() for validation-only constructors
- isRecord() helper method
- JDocComment.addParam(JRecordComponent) for javadoc support

Tests cover: basic records, empty records, object/array/
varargs components, generics, annotations, compact and canonical
constructors, methods, static members, nested records, and javadoc.
- Change from ValueEnforcer to explicit IllegalStateException throws
- Fix missing semicolons in throw statements
- Fix error message in recordComponentVararg()
- Add tests for calling record methods on non-record classes
- Add test for duplicate compact constructor
Add support for Java records (JEP 395, Java 16+)
@phax phax merged commit 4556df1 into master Feb 3, 2026
4 checks passed
@phax phax deleted the records branch February 3, 2026 11:06
@glelouet
Copy link
Contributor

glelouet commented Feb 3, 2026

The record components should be used as variables too.

JRecordTest::testRecordWithCompactConstructor should use record components directly

		JRecordComponent lo = rec.recordComponent (cm.INT, "lo");
		JRecordComponent hi = rec.recordComponent (cm.INT, "hi");
 …
		compactCtor.body ()
							 ._if (lo.gt (hi))

Same on the next test methods testRecordWithCanonicalConstructor

Make JRecordComponent implement IJExpression ? Only default methods AFAIK, parent IJGenerable::generate already implemented since JRecordComponent implements IJGenerable.

@phax
Copy link
Owner Author

phax commented Feb 3, 2026

Yes, I've seen this too and tried making JRecordComponent and IJAssignmentTarget but it lead to invalid code. I guess we would need a JRecordComponentRef class instead

@glelouet
Copy link
Contributor

glelouet commented Feb 3, 2026

can't be assignment target, it's const.

@phax
Copy link
Owner Author

phax commented Feb 3, 2026

Inside the constructor it is assigned to

@glelouet
Copy link
Contributor

glelouet commented Feb 3, 2026

Yeah, also in future "with" it will be.

phax added a commit that referenced this pull request Feb 3, 2026
@phax
Copy link
Owner Author

phax commented Feb 3, 2026

@glelouet better?

@glelouet
Copy link
Contributor

glelouet commented Feb 3, 2026

What do you mean by inside the constructor through ?
@phax

Ho I get it. Constructors of the record can set their own value, eg

Range(T min, T max){
 Range(Pair<T> p){
   this.min=p.first;
  this.max=p.second;
 }
}

@phax
Copy link
Owner Author

phax commented Feb 3, 2026

Syntactic sugar with fixed coding styleguide for getters and setters ;-)

@glelouet
Copy link
Contributor

glelouet commented Feb 3, 2026

@phax The modified parts are better indeed, but the rest of the test is still to be changed ? (Not complaining, just stating)

@phax
Copy link
Owner Author

phax commented Feb 3, 2026

Feel free to complain ;-) Noted

@glelouet
Copy link
Contributor

glelouet commented Feb 3, 2026

Syntactic sugar with fixed coding styleguide for getters and setters ;-)

Yeah I like that refthis can take a record into account. Something required when doing, say, record constructors :P

@glelouet
Copy link
Contributor

glelouet commented Feb 3, 2026

Feel free to complain ;-) Noted

No point in complaining if that's not brought alongside an improvement effort.

@glelouet
Copy link
Contributor

glelouet commented Feb 3, 2026

@phax

As a side note can we also have a throw() expression that takes a Class<? extends exception> or a codemodelreference as a parameter ?

I just realize the

				._throw (JExpr._new (cm.ref (IllegalArgumentException.class)));

Could definitely be improved into

				._throw (IllegalArgumentException.class);

If JBlock had a sugar coating corresponding method.

@phax
Copy link
Owner Author

phax commented Feb 3, 2026

Will do it later - have other priorities atm

@glelouet
Copy link
Contributor

glelouet commented Feb 3, 2026

There is no codemodel reference in the JBlock so only using AbstractJClass ; added second method in JBlock :

	@NonNull
	public JThrow _throw (@NonNull final IJExpression aExpr)
	{
		return internalInsert (new JThrow (aExpr));
	}
	
	@NonNull
	public JThrow _throw (@NonNull final AbstractJClass throwClass)
	{
		return _throw (JExpr._new( throwClass));
	}

Only saves a _new call, not sure if worth.

@glelouet
Copy link
Contributor

glelouet commented Feb 3, 2026

@phax can you show me where it is allowed to assign values to the record fields ?

I could not find a way in https://docs.oracle.com/en/java/javase/25/language/records.html#GUID-6699E26F-4A9B-4393-A08B-1E47D4B2D263

On the opposite, I found

You can define alternative, noncanonical constructors whose argument list doesn't match the record's type parameters. However, these constructors must invoke the record's canonical constructor.

Which implies that there is no assignment ?

edit : okay it's in the cannonical constructor.

        this.length = length;
        this.width = width;

phax added a commit that referenced this pull request Feb 3, 2026
phax added a commit that referenced this pull request Feb 3, 2026
@phax
Copy link
Owner Author

phax commented Feb 3, 2026

Took your proposal in - seems like a good compromise

@glelouet
Copy link
Contributor

glelouet commented Feb 3, 2026

maybe could even add a varagar of params to pass to the _new call.
example use case _throw(cm.ref(UnsupportedOperationException.class), Jexpr.lit("case not supported") );

Just an idea though.

=> (only second method changed)

	@NonNull
	public JThrow _throw (@NonNull final IJExpression aExpr)
	{
		return internalInsert (new JThrow (aExpr));
	}
	
	@NonNull
	public JThrow _throw (@NonNull final AbstractJClass throwClass, IJExpression... params)
	{
		JInvocation init = JExpr._new( throwClass);
		if(params!=null )
		{
			for(IJExpression ije : params)
			{
				init.arg(ije);
			}
		}
		return _throw (init);
	}

phax added a commit that referenced this pull request Feb 3, 2026
@phax
Copy link
Owner Author

phax commented Feb 3, 2026

Good idea - done. But using a separate method

@glelouet
Copy link
Contributor

glelouet commented Feb 3, 2026

@phax the null test was because the varargs was allowed null.
If you split in two cases : no varargs XOR varagars nonnull, no need to test for nullity in the second one.

@phax
Copy link
Owner Author

phax commented Feb 3, 2026

you can't have enough null checks ;-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants