or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

attributes.mdbean-style.mdcode-generation.mdcollections.mdcore-immutable.mdindex.mdintegrations.mdjson-marshaling.mdruntime-marshaling.mdstyling.md

attributes.mddocs/

0

# Attribute Customization

1

2

Annotations for customizing individual attributes including default values, lazy computation, validation, and parameter ordering. These annotations provide fine-grained control over how each attribute behaves in generated immutable classes.

3

4

## Capabilities

5

6

### Default Values

7

8

Provide default values for optional attributes in builders.

9

10

```java { .api }

11

/**

12

* Marks a method as providing a default value for an attribute.

13

* The method body will be used as the default value in builders

14

* when the attribute is not explicitly set.

15

*/

16

@Target(ElementType.METHOD)

17

@Retention(RetentionPolicy.SOURCE)

18

@interface Value.Default {}

19

```

20

21

**Usage Example:**

22

23

```java

24

import org.immutables.value.Value;

25

import java.time.Duration;

26

27

@Value.Immutable

28

public interface ServerConfig {

29

String host();

30

31

@Value.Default

32

default int port() {

33

return 8080;

34

}

35

36

@Value.Default

37

default boolean ssl() {

38

return false;

39

}

40

41

@Value.Default

42

default Duration timeout() {

43

return Duration.ofSeconds(30);

44

}

45

}

46

47

// Usage - port, ssl, and timeout use defaults

48

ServerConfig config = ImmutableServerConfig.builder()

49

.host("localhost")

50

.build();

51

```

52

53

### Derived Attributes

54

55

Computed attributes that are stored in fields for performance.

56

57

```java { .api }

58

/**

59

* Marks an attribute as derived/computed. The method implementation

60

* provides the computation logic, and the result is cached in a field

61

* for performance.

62

*/

63

@Target(ElementType.METHOD)

64

@Retention(RetentionPolicy.SOURCE)

65

@interface Value.Derived {}

66

```

67

68

**Usage Example:**

69

70

```java

71

import org.immutables.value.Value;

72

import java.time.LocalDate;

73

import java.time.Period;

74

75

@Value.Immutable

76

public interface Person {

77

String firstName();

78

String lastName();

79

LocalDate birthDate();

80

81

@Value.Derived

82

default String fullName() {

83

return firstName() + " " + lastName();

84

}

85

86

@Value.Derived

87

default int age() {

88

return Period.between(birthDate(), LocalDate.now()).getYears();

89

}

90

}

91

92

// Derived attributes are computed once and cached

93

Person person = ImmutablePerson.builder()

94

.firstName("Alice")

95

.lastName("Smith")

96

.birthDate(LocalDate.of(1990, 5, 15))

97

.build();

98

99

String name = person.fullName(); // Computed once, cached

100

int age = person.age(); // Computed once, cached

101

```

102

103

### Lazy Computation

104

105

Thread-safe lazy computed attributes for expensive operations.

106

107

```java { .api }

108

/**

109

* Marks an attribute as lazily computed. The computation is deferred

110

* until first access and is thread-safe. Use for expensive computations

111

* that may not always be needed.

112

*/

113

@Target(ElementType.METHOD)

114

@Retention(RetentionPolicy.SOURCE)

115

@interface Value.Lazy {}

116

```

117

118

**Usage Example:**

119

120

```java

121

import org.immutables.value.Value;

122

import java.util.List;

123

124

@Value.Immutable

125

public interface DataSet {

126

List<Double> values();

127

128

@Value.Lazy

129

default double mean() {

130

return values().stream()

131

.mapToDouble(Double::doubleValue)

132

.average()

133

.orElse(0.0);

134

}

135

136

@Value.Lazy

137

default double standardDeviation() {

138

double mean = mean();

139

return Math.sqrt(

140

values().stream()

141

.mapToDouble(v -> Math.pow(v - mean, 2))

142

.average()

143

.orElse(0.0)

144

);

145

}

146

}

147

148

// Expensive computations are deferred until needed

149

DataSet data = ImmutableDataSet.builder()

150

.addValues(1.0, 2.0, 3.0, 4.0, 5.0)

151

.build();

152

153

// Only computed when first accessed, then cached

154

double mean = data.mean();

155

double stdDev = data.standardDeviation();

156

```

157

158

### Constructor Parameters

159

160

Control parameter ordering and inclusion in generated constructors.

161

162

```java { .api }

163

/**

164

* Marks an accessor method as a constructor parameter.

165

* Controls the order of parameters in generated constructors

166

* and factory methods.

167

*/

168

@Target(ElementType.METHOD)

169

@Retention(RetentionPolicy.SOURCE)

170

@interface Value.Parameter {

171

/** Order of parameter in constructor (lower values come first) */

172

int order() default 0;

173

}

174

```

175

176

**Usage Example:**

177

178

```java

179

@Value.Immutable

180

public interface Point3D {

181

@Value.Parameter(order = 1)

182

double x();

183

184

@Value.Parameter(order = 2)

185

double y();

186

187

@Value.Parameter(order = 3)

188

double z();

189

190

// Not a constructor parameter

191

@Value.Derived

192

default double magnitude() {

193

return Math.sqrt(x() * x() + y() * y() + z() * z());

194

}

195

}

196

197

// Generated constructor respects parameter ordering

198

Point3D point = ImmutablePoint3D.of(1.0, 2.0, 3.0); // x, y, z order

199

```

200

201

### Validation

202

203

Validation method invocation for instance validation.

204

205

```java { .api }

206

/**

207

* Marks a method as a validation method. The method will be called

208

* during instance construction to validate the object state.

209

* Should throw an exception if validation fails.

210

*/

211

@Target(ElementType.METHOD)

212

@Retention(RetentionPolicy.SOURCE)

213

@interface Value.Check {}

214

```

215

216

**Usage Example:**

217

218

```java

219

@Value.Immutable

220

public interface Rectangle {

221

double width();

222

double height();

223

224

@Value.Check

225

default void validate() {

226

if (width() <= 0) {

227

throw new IllegalArgumentException("Width must be positive");

228

}

229

if (height() <= 0) {

230

throw new IllegalArgumentException("Height must be positive");

231

}

232

}

233

234

@Value.Derived

235

default double area() {

236

return width() * height();

237

}

238

}

239

240

// Validation is automatically called during construction

241

try {

242

Rectangle rect = ImmutableRectangle.builder()

243

.width(-1.0) // Invalid!

244

.height(5.0)

245

.build(); // Throws IllegalArgumentException

246

} catch (IllegalArgumentException e) {

247

// Handle validation error

248

}

249

```

250

251

### Auxiliary Attributes

252

253

Exclude attributes from equals, hashCode, and toString methods.

254

255

```java { .api }

256

/**

257

* Marks an attribute as auxiliary. Auxiliary attributes are excluded

258

* from equals(), hashCode(), and toString() methods but are still

259

* part of the immutable object.

260

*/

261

@Target(ElementType.METHOD)

262

@Retention(RetentionPolicy.SOURCE)

263

@interface Value.Auxiliary {}

264

```

265

266

**Usage Example:**

267

268

```java

269

@Value.Immutable

270

public interface CacheEntry {

271

String key();

272

String value();

273

274

@Value.Auxiliary // Not part of equality/hash

275

Instant createdAt();

276

277

@Value.Auxiliary // Not part of equality/hash

278

int accessCount();

279

}

280

281

// Two entries with same key/value are equal regardless of auxiliary fields

282

CacheEntry entry1 = ImmutableCacheEntry.builder()

283

.key("user:123")

284

.value("Alice")

285

.createdAt(Instant.now())

286

.accessCount(5)

287

.build();

288

289

CacheEntry entry2 = ImmutableCacheEntry.builder()

290

.key("user:123")

291

.value("Alice")

292

.createdAt(Instant.now().minusSeconds(10)) // Different time

293

.accessCount(2) // Different count

294

.build();

295

296

// Still equal because auxiliary fields are ignored

297

assert entry1.equals(entry2); // true

298

assert entry1.hashCode() == entry2.hashCode(); // true

299

```

300

301

## Combining Annotations

302

303

Multiple attribute annotations can be combined for complex behavior:

304

305

```java

306

@Value.Immutable

307

public interface ComplexType {

308

String name();

309

310

@Value.Default

311

@Value.Parameter(order = 1)

312

default String category() {

313

return "default";

314

}

315

316

@Value.Lazy

317

@Value.Auxiliary

318

default String expensiveComputation() {

319

// Expensive operation not included in equals/hash

320

return performExpensiveCalculation();

321

}

322

323

@Value.Derived

324

@Value.Check

325

default String normalizedName() {

326

String normalized = name().toLowerCase().trim();

327

if (normalized.isEmpty()) {

328

throw new IllegalArgumentException("Name cannot be empty");

329

}

330

return normalized;

331

}

332

}

333

```