@@ -142,6 +142,16 @@ public class JDefinedClass extends AbstractJClassContainer <JDefinedClass> imple
142142 */
143143 private final Map <String , JEnumConstant > m_aEnumConstantsByName = new LinkedHashMap <> ();
144144
145+ /**
146+ * Record components for record types. Order is significant.
147+ */
148+ private final List <JRecordComponent > m_aRecordComponents = new ArrayList <> ();
149+
150+ /**
151+ * Compact constructor for record types, if defined.
152+ */
153+ private JMethod m_aCompactConstructor ;
154+
145155 /**
146156 * Annotations on this variable. Lazily created.
147157 */
@@ -342,7 +352,7 @@ public JEnumConstant enumConstant (@NonNull final String sName)
342352
343353 /**
344354 * Check if a specific enum constant name is already in use
345- *
355+ *
346356 * @param sName
347357 * Enum constant name to check. May not be <code>null</code>.
348358 * @return <code>true</code> if it is contained, <code>false</code> otherwise
@@ -352,6 +362,136 @@ public boolean containsEnumConstant (@NonNull final String sName)
352362 return m_aEnumConstantsByName .containsKey (sName );
353363 }
354364
365+ /**
366+ * Adds a component to this record.
367+ *
368+ * @param aType
369+ * Type of the component. May not be <code>null</code>.
370+ * @param sName
371+ * Name of the component. May not be <code>null</code>.
372+ * @return The newly created record component.
373+ * @throws IllegalStateException
374+ * If this is not a record type.
375+ * @since 4.2.0
376+ */
377+ @ NonNull
378+ public JRecordComponent recordComponent (@ NonNull final AbstractJType aType , @ NonNull final String sName )
379+ {
380+ if (!isRecord ())
381+ throw new IllegalStateException ("recordComponent() is only valid for record types" );
382+ ValueEnforcer .notNull (aType , "Type" );
383+ ValueEnforcer .notNull (sName , "Name" );
384+
385+ final JRecordComponent comp = new JRecordComponent (this , aType , sName , false );
386+ m_aRecordComponents .add (comp );
387+ return comp ;
388+ }
389+
390+ /**
391+ * Adds a component to this record using a Class reference.
392+ *
393+ * @param aType
394+ * Type of the component. May not be <code>null</code>.
395+ * @param sName
396+ * Name of the component. May not be <code>null</code>.
397+ * @return The newly created record component.
398+ * @throws IllegalStateException
399+ * If this is not a record type.
400+ * @since 4.2.0
401+ */
402+ @ NonNull
403+ public JRecordComponent recordComponent (@ NonNull final Class <?> aType , @ NonNull final String sName )
404+ {
405+ return recordComponent (owner ().ref (aType ), sName );
406+ }
407+
408+ /**
409+ * Adds a varargs component to this record. This must be the last component.
410+ *
411+ * @param aType
412+ * The component type of the varargs array. May not be <code>null</code>.
413+ * @param sName
414+ * Name of the component. May not be <code>null</code>.
415+ * @return The newly created record component.
416+ * @throws IllegalStateException
417+ * If this is not a record type.
418+ * @since 4.2.0
419+ */
420+ @ NonNull
421+ public JRecordComponent recordComponentVararg (@ NonNull final AbstractJType aType , @ NonNull final String sName )
422+ {
423+ if (!isRecord ())
424+ throw new IllegalStateException ("recordComponentVararg() is only valid for record types" );
425+ ValueEnforcer .notNull (aType , "Type" );
426+ ValueEnforcer .notNull (sName , "Name" );
427+
428+ final JRecordComponent comp = new JRecordComponent (this , aType .array (), sName , true );
429+ m_aRecordComponents .add (comp );
430+ return comp ;
431+ }
432+
433+ /**
434+ * @return The list of record components. Never <code>null</code>.
435+ * @since 4.2.0
436+ */
437+ @ NonNull
438+ public List <JRecordComponent > recordComponents ()
439+ {
440+ return Collections .unmodifiableList (m_aRecordComponents );
441+ }
442+
443+ /**
444+ * Creates a compact constructor for this record. A compact constructor has no
445+ * parameter list and is used for validation or normalization of record components.
446+ * <p>
447+ * Example:
448+ * <pre>
449+ * public record Range(int lo, int hi) {
450+ * public Range {
451+ * if (lo > hi) throw new IllegalArgumentException();
452+ * }
453+ * }
454+ * </pre>
455+ *
456+ * @param nMods
457+ * Modifiers for this constructor (typically {@link JMod#PUBLIC}).
458+ * @return The newly created compact constructor.
459+ * @throws IllegalStateException
460+ * If this is not a record type or a compact constructor already exists.
461+ * @since 4.2.0
462+ */
463+ @ NonNull
464+ public JMethod compactConstructor (final int nMods )
465+ {
466+ if (!isRecord ())
467+ throw new IllegalStateException ("compactConstructor() is only valid for record types" );
468+
469+ if (m_aCompactConstructor != null )
470+ throw new IllegalStateException ("A compact constructor has already been defined" );
471+
472+ m_aCompactConstructor = new JMethod (nMods , this );
473+ return m_aCompactConstructor ;
474+ }
475+
476+ /**
477+ * @return The compact constructor if defined, <code>null</code> otherwise.
478+ * @since 4.2.0
479+ */
480+ @ Nullable
481+ public JMethod compactConstructor ()
482+ {
483+ return m_aCompactConstructor ;
484+ }
485+
486+ /**
487+ * @return <code>true</code> if this is a record type.
488+ * @since 4.2.0
489+ */
490+ public boolean isRecord ()
491+ {
492+ return getClassType () == EClassType .RECORD ;
493+ }
494+
355495 @ Override
356496 public String binaryName ()
357497 {
@@ -665,14 +805,31 @@ public void declare (@NonNull final IJFormatter f)
665805 f .generable (aAnnotation ).newline ();
666806
667807 // Modifiers (private, protected, public)
668- // Type of class (class, interface, enum, @interface)
808+ // Type of class (class, interface, enum, @interface, record )
669809 // Name of the class
670810 // Class wildcards
671811 f .generable (m_aMods ).print (getClassType ().declarationToken ()).id (name ()).declaration (m_aGenerifiable );
672812
813+ // For records, output the component list
814+ if (isRecord ())
815+ {
816+ f .print ('(' );
817+ boolean bFirst = true ;
818+ for (final JRecordComponent comp : m_aRecordComponents )
819+ {
820+ if (bFirst )
821+ bFirst = false ;
822+ else
823+ f .print (',' );
824+ f .generable (comp );
825+ }
826+ f .print (')' );
827+ }
828+
673829 // If a super class is defined and is not "Object"
830+ // Note: Records cannot extend classes (they implicitly extend java.lang.Record)
674831 boolean bHasSuperClass = false ;
675- if (m_aSuperClass != null && m_aSuperClass != owner ().ref (Object .class ))
832+ if (! isRecord () && m_aSuperClass != null && m_aSuperClass != owner ().ref (Object .class ))
676833 {
677834 bHasSuperClass = true ;
678835 f .newline ().indent ().print ("extends" ).generable (m_aSuperClass ).newline ().outdent ();
@@ -727,7 +884,23 @@ protected void declareBody (@NonNull final IJFormatter f)
727884 if (m_aInstanceInit != null )
728885 f .newline ().statement (m_aInstanceInit );
729886
730- // All constructors
887+ // Compact constructor for records (special syntax without parameter list)
888+ if (m_aCompactConstructor != null )
889+ {
890+ f .newline ();
891+ // Output javadoc if present
892+ if (m_aCompactConstructor .hasJavadoc ())
893+ f .generable (m_aCompactConstructor .javadoc ());
894+ // Output annotations
895+ for (final JAnnotationUse annotation : m_aCompactConstructor .annotations ())
896+ f .generable (annotation ).newline ();
897+ // Output modifiers and name only (no parentheses)
898+ f .generable (m_aCompactConstructor .mods ()).id (name ());
899+ // Output body
900+ f .statement (m_aCompactConstructor .body ());
901+ }
902+
903+ // All regular constructors
731904 for (final JMethod m : m_aConstructors )
732905 f .newline ().declaration (m );
733906
0 commit comments