forked from immutables/immutables.github.io
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathencoding.html
573 lines (485 loc) · 59.6 KB
/
encoding.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<meta http-equiv="X-UA-Compatible" content="chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!--link rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.0/css/bootstrap.min.css"-->
<link rel="stylesheet" href="/gfx/bootstrap.min.css">
<link rel="stylesheet" href="/gfx/main.css">
<link rel="stylesheet" href="/gfx/code.css">
<title>Encoding custom types</title>
</head>
<body class="page">
<!-- Google Tag Manager -->
<noscript><iframe src="//www.googletagmanager.com/ns.html?id=GTM-PMJSKV"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'//www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-PMJSKV');</script>
<!-- End Google Tag Manager -->
<header>
<div class="container">
<a href="/">Immutables</a> ←
<h1>Encoding custom types <a class="github-button" href="https://github.com/immutables/immutables" data-style="mega" data-count-href="/immutables/immutables/stargazers" data-count-api="/repos/immutables/immutables#stargazers_count" data-count-aria-label="# stargazers on GitHub" aria-label="Star immutables/immutables on GitHub">stars</a></h1>
</div>
</header>
<aside id="toc"></aside>
<section class="documentation">
<h2>Introduction</h2>
<p>The <em>Immutables</em> annotation processor supports not only plain <a href="immutable.html#attributes">attribute</a> accessors but also provides additional conveniences when using special types like collections, maps, optional types. For instance, generated builders contain methods to add collection elements one by one or add optional element without having to wrap it explicitly (See <a href="immutable.html#array-collection-and-map-attributes">collection</a>, <a href="http://immutables.github.io/immutable.html#optional-attributes">optional</a> etc). But this built-in support is limited only to a handful of predefined classes and interfaces, such as <code>List</code>, <code>Map</code>, <code>Optional</code>, <code>Multimap</code>, <code>ImmutableSet</code>...</p>
<p>Obviously, it would desirable to have support for a variety of popular immutable collection libraries or custom made wrapper types in a way similar to those supported out of the box. Or, for example, the way optional types are handled may be not the way how you would encode it. Luckily, we have this covered!</p>
<p>New experimental functionality allows you to create encoding classes: annotated java classes which serve as examples, snippets of code to be generated. Yes, don't need to dive into annotation processing API, nor to craft obscure code-generation templates! Just use plain java code (with some reasonable limitations and rules) to describe how to embed attributes of particular type into generated immutable class. Encoding classes are compiled to metadata annotations which can be packed as reusable jar libraries of annotation processor extensions.</p>
<h2>Tutorial</h2>
<p>We'll dive straight into practical example which will demonstrate typical use case as well as the most important pieces of the functionality in a step-by-step fashion. You can skip to the <a href="#howto">How to</a> if looking for specific recipes.</p>
<p><strong>Let's create encoding for the <code>com.google.common.collect.ImmutableTable</code></strong></p>
<h3>Setting up projects</h3>
<p>Start by creating modules for our encoding. One module to create encoding itself, and another one to use apply it to generated objects. (<a href="#why-separate">See why need for separate modules</a>)</p>
<div class="highlight"><pre><code class="language-text" data-lang="text"><span></span>encoding-defs/
+-src/ (think of it as as src/main/java, but simpler ;)
| +-encoding/ (simply a package)
| +-TableEncoding.java (encoding file)
+-pom.xml
encoding-use/
+-src/
| +-uses/
| +-UseTable.java (value object that is using the encoding)
+-pom.xml
</code></pre></div>
<p>We'll progress by gradually editing files and compiling projects.</p>
<p>Here's the Immutable modules we will use</p>
<ul>
<li><a href="http://search.maven.org/#artifactdetails%7Corg.immutables%7Cvalue%7C2.6.3%7Cjar">org.immutables:value:2.6.3</a>
<ul>
<li>the annotation processor used to compile encodings and value objects</li>
</ul></li>
<li><a href="http://search.maven.org/#artifactdetails%7Corg.immutables%7Cencoding%7C2.6.3%7Cjar">org.immutables:encode:2.6.3</a>
<ul>
<li>the annotation API to define encoding classes</li>
</ul></li>
</ul>
<p>Maven dependencies will look like following snippets:</p>
<div class="highlight"><pre><code class="language-xml" data-lang="xml"><span></span><span class="c"><!-- dependencies for 'encoding-def' module --></span>
<span class="nt"><dependency></span>
<span class="c"><!-- the annotation processor, compile only --></span>
<span class="nt"><groupId></span>org.immutables<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>value<span class="nt"></artifactId></span>
<span class="nt"><version></span>2.6.3<span class="nt"></version></span>
<span class="nt"><scope></span>provided<span class="nt"></scope></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="c"><!-- annotation to encodings, need to be reexported transitively, so annotation can be read at compile time for using modules --></span>
<span class="nt"><groupId></span>org.immutables<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>encode<span class="nt"></artifactId></span>
<span class="nt"><version></span>2.6.3<span class="nt"></version></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="c"><!-- we'll encode ImmutableTable, so we need guava dependency, while user of the encoding will have to reference at least Table/ImmutableTable we can skip reexport,</span>
<span class="c"> relying on the using module have it's own guava dependency --></span>
<span class="nt"><groupId></span>com.google.guava<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>guava<span class="nt"></artifactId></span>
<span class="nt"><version></span>20.0<span class="nt"></version></span>
<span class="nt"><scope></span>provided<span class="nt"></scope></span>
<span class="nt"></dependency></span>
</code></pre></div><div class="highlight"><pre><code class="language-xml" data-lang="xml"><span></span><span class="c"><!-- dependencies for 'encoding-use' module --></span>
<span class="nt"><dependency></span>
<span class="c"><!-- the annotation processor, compile only --></span>
<span class="nt"><groupId></span>org.immutables<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>value<span class="nt"></artifactId></span>
<span class="nt"><version></span>2.6.3<span class="nt"></version></span>
<span class="nt"><scope></span>provided<span class="nt"></scope></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="c"><!-- Use encoding defined in 'encoding-def' --></span>
<span class="nt"><groupId></span>org.immutables.sample<span class="nt"></groupId></span> <span class="c"><!-- or whatever group you choose for sibling sample projects --></span>
<span class="nt"><artifactId></span>encoding-def<span class="nt"></artifactId></span>
<span class="nt"><version></span>1-SNAPSHOT<span class="nt"></version></span> <span class="c"><!-- whatever version we use for sample modules --></span>
<span class="nt"><scope></span>provided<span class="nt"></scope></span> <span class="c"><!-- encoding definitions and annotations are compile only --></span>
<span class="nt"></dependency></span>
<span class="nt"><dependency></span>
<span class="c"><!-- compile and runtime dependency on Guava as we use Table/ImmutableTable classes --></span>
<span class="nt"><groupId></span>com.google.guava<span class="nt"></groupId></span>
<span class="nt"><artifactId></span>guava<span class="nt"></artifactId></span>
<span class="nt"><version></span>20.0<span class="nt"></version></span>
<span class="nt"></dependency></span>
</code></pre></div>
<p>I trust you can figure out corresponding configuration for Gradle or other build systems (but it would be great if people could contribute it to this tutorial!).</p>
<p>If you need more detailed setup examples on how to setup the build, please, <a href="https://github.com/immutables/samples">see complete sample projects encoding-*</a>.</p>
<h3>First encoding</h3>
<p>Let's create package and class for the <code>Table</code> encoding. It could be <code>public</code>, but there's no need for it to be visible outside, so package-private visibility is most appropriate. (Going forward, there will be lot of places where package-private visibility will be used, but when actual code is generated <code>public</code> or whatever appropriate will be used)</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="kn">package</span> <span class="nn">encoding</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.immutables.encode.Encoding</span><span class="o">;</span>
<span class="nd">@Encoding</span>
<span class="kd">class</span> <span class="nc">TableEncoding</span> <span class="o">{</span>
<span class="o">}</span>
</code></pre></div>
<p>Once compiled (by saying "compiled" we will usually mean something straightforward like <code>mvn clean install</code>), there will be an error reported:</p>
<div class="highlight"><pre><code class="language-text" data-lang="text"><span></span>[ERROR] ../samples/encoding-def/src/encoding/TableEncoding.java:[6,1] @Encoding.Impl field is bare minimum to be declared. Please add implementation field declaration
</code></pre></div>
<p>Ok, so the bare minimum to be declared is a so called implementation field. Indeed, the system need to know some minimum information about what we're actually encoding. We have to declare the type we trying to handle as well as how we would store its instances internally. Luckily, this is straightforward, here's how we will define implementation field:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="kn">package</span> <span class="nn">encoding</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.immutables.encode.Encoding</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.google.common.collect.ImmutableTable</span><span class="o">;</span>
<span class="nd">@Encoding</span>
<span class="kd">class</span> <span class="nc">TableEncoding</span> <span class="o">{</span>
<span class="nd">@Encoding.Impl</span>
<span class="kd">private</span> <span class="n">ImmutableTable</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">></span> <span class="n">field</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div>
<p>And it compiles now successfully! But wait, what do we have achieved? Before answering this, let's actually use our encoding. Create <code>uses/UseTable.java</code> in <code>encoding-use</code> module like shown below:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="kn">package</span> <span class="nn">uses</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.immutables.value.Value</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.google.common.collect.ImmutableTable</span><span class="o">;</span>
<span class="nd">@Value.Immutable</span>
<span class="kd">interface</span> <span class="nc">UseTable</span> <span class="o">{</span>
<span class="n">ImmutableTable</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">></span> <span class="nf">values</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div>
<p>If we compile this, <code>ImmutableUseTable</code> type will be generated, but looking at the generated code you won't see anything changed, or anything that looks like specially encoded. We need to activate encoding in order for it to have any effect on the generated code.</p>
<p>The trick is that encoding we've created generates activation annotation which has all the encoding definition code "compiled" and attached to it as metadata. Looking at the generated sources for our <code>encoding-def</code> module, you'll see following annotation class:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="c1">// this is the sample listing of the generated file</span>
<span class="c1">// target/generated-sources/annotations/encoding/TableEncodingEnabled.java</span>
<span class="kn">package</span> <span class="nn">encoding</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.immutables.encode.EncodingMetadata</span><span class="o">;</span>
<span class="nd">@EncodingMetadata</span><span class="o">(</span>
<span class="n">name</span> <span class="o">=</span> <span class="s">"encoding.TableEncoding"</span><span class="o">,</span>
<span class="n">imports</span> <span class="o">=</span> <span class="o">{</span>
<span class="o">},</span>
<span class="n">typeParams</span> <span class="o">=</span> <span class="o">{},</span>
<span class="n">elements</span> <span class="o">=</span> <span class="o">{</span>
<span class="nd">@EncodingMetadata.Element</span><span class="o">(</span>
<span class="n">name</span> <span class="o">=</span> <span class="s">"value"</span><span class="o">,</span>
<span class="n">tags</span> <span class="o">=</span> <span class="o">{</span><span class="s">"IMPL"</span><span class="o">,</span> <span class="s">"PRIVATE"</span><span class="o">,</span> <span class="s">"FINAL"</span><span class="o">,</span> <span class="s">"FIELD"</span><span class="o">},</span>
<span class="n">naming</span> <span class="o">=</span> <span class="s">"*"</span><span class="o">,</span>
<span class="c1">// ... many lines skipped here</span>
<span class="kd">public</span> <span class="nd">@interface</span> <span class="n">TableEncodingEnabled</span> <span class="o">{}</span>
</code></pre></div>
<p>Use <code>TableEncodingEnabled</code> annotation to activate encoding. It can be placed on the value type itself or on the package affecting all value types in the package. Placed parent package it will affect all nested packages in a current compilation module. The activation annotation can be used also as meta-annotation, see <a href="#meta-annotations">enabling encoding via meta-annotations</a>.</p>
<p>As placing encoding annotation on the type directly is pretty lame (in the sense of cluttering value objects with configuration), we'll place it on the <code>uses</code> package affecting all value types in the package.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="c1">// create encoding-use/src/uses/package-info.java</span>
<span class="nd">@encoding.TableEncodingEnabled</span> <span class="c1">// <-- this will activate the encoding</span>
<span class="kn">package</span> <span class="nn">uses</span><span class="o">;</span>
</code></pre></div>
<p>After successful re-compilation of <code>encoding-use</code> module we are ready to see we achieved to apply our minimal encoding of <code>ImmutableTable<String,String,String></code>. Indeed, generated code of <code>ImmutableUseTable.java</code> is a little bit different internally from what was generated before we've applied the encoding. The great thing is that we've able to properly setup projects and apply encoding, but, otherwise, we are yet to see anything useful about an encodings: there are no externally observable changes. We have to start creating useful definitions on top of the minimal encoding to unleash the power.</p>
<h3>Type parameters</h3>
<p>The first thing that should bother us is that the encoding only applies to <code>ImmutableTable<String,String,String></code>, i.e. exactly to the specified type arguments. If we add another accessor which will use <code>Integer</code> type arguments, the encoding will not be applied.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="nd">@Value.Immutable</span>
<span class="kd">interface</span> <span class="nc">UseTable</span> <span class="o">{</span>
<span class="n">ImmutableTable</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">></span> <span class="nf">values</span><span class="o">();</span> <span class="c1">// <-- encoding applied</span>
<span class="n">ImmutableTable</span><span class="o"><</span><span class="n">Integer</span><span class="o">,</span> <span class="n">Integer</span><span class="o">,</span> <span class="n">Integer</span><span class="o">></span> <span class="nf">intValues</span><span class="o">();</span> <span class="c1">// <-- default code is generated</span>
<span class="o">}</span>
</code></pre></div>
<p>To make encoding flexible about type arguments we'll use generic parameters on encoding.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="kn">package</span> <span class="nn">encoding</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.immutables.encode.Encoding</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.google.common.collect.ImmutableTable</span><span class="o">;</span>
<span class="nd">@Encoding</span>
<span class="kd">class</span> <span class="nc">TableEncoding</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="o">{</span> <span class="c1">// <-- introduce type parameters</span>
<span class="nd">@Encoding.Impl</span>
<span class="kd">private</span> <span class="n">ImmutableTable</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="n">field</span><span class="o">;</span> <span class="c1">// <-- use them anywhere we reference the type</span>
<span class="o">}</span>
</code></pre></div>
<p>After recompiling both <code>encoding-def</code> and <code>encoding-use</code> modules, both accessors of the <code>ImmutableUseTable</code> class will be also implemented by our encoding. And so <code>TableEncoding</code> will be applied to any type arguments of <code>ImmutableTable</code> in a scope where it's applied. You can also safely assume that encoding will also capture any <code>ImmutableTable</code> arguments which themselves are type variables. If we parametrize <code>UseTable</code>, our encoding will still apply:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="nd">@Value.Immutable</span>
<span class="kd">interface</span> <span class="nc">UseTable</span><span class="o"><</span><span class="n">V</span><span class="o">></span> <span class="o">{</span> <span class="c1">// <-- introduce type variable</span>
<span class="n">ImmutableTable</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="nf">values</span><span class="o">();</span> <span class="c1">// <-- encoding applied</span>
<span class="n">ImmutableTable</span><span class="o"><</span><span class="n">Integer</span><span class="o">,</span> <span class="n">Integer</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="nf">intValues</span><span class="o">();</span> <span class="c1">// <-- encoding applied</span>
<span class="o">}</span>
</code></pre></div>
<h3>Exposed type and accessors</h3>
<p>It's not uncommon to see value interfaces (or abstract classes) implemented by both immutable and mutable classes. While we'll leave mutable implementations out of this discussion, but, at minimum, we'll want to apply <code>ImmutableTable</code> encoding as implementation to attributes exposed as <code>com.google.common.collect.Table</code> interface. The encoding we've created contains only the implementation field. The type, to which the encoding applies to, is derived directly from the field. Fortunately, we're able to specify more general types for encoding as long as they are compatible.</p>
<p>The recipe is following: create no-arg accessors with target return types and use <code>@Encoding.Expose</code> annotation to mark these accessors. The names of the accessors are irrelevant as long as they are unambiguous. And in our case, it should be obvious that they would return the value of the <code>value</code> fields. Here's how our encoding would look like after adding <code>Expose</code> accessors:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="kn">package</span> <span class="nn">encoding</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.google.common.collect.ImmutableTable</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">com.google.common.collect.Table</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.immutables.encode.Encoding</span><span class="o">;</span>
<span class="nd">@Encoding</span>
<span class="kd">class</span> <span class="nc">TableEncoding</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="o">{</span>
<span class="nd">@Encoding.Impl</span>
<span class="kd">private</span> <span class="n">ImmutableTable</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="n">field</span><span class="o">;</span>
<span class="nd">@Encoding.Expose</span>
<span class="n">ImmutableTable</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="nf">getImmutableTable</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">field</span><span class="o">;</span> <span class="c1">// <-- this is how our accessor would be implemented</span>
<span class="o">}</span>
<span class="nd">@Encoding.Expose</span>
<span class="n">Table</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="nf">getTable</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">field</span><span class="o">;</span> <span class="c1">// <-- this is how our accessor would be implemented</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<p>Important point about this is that as we define at least one such <em>expose</em> accessor, no type would be derived from the field. In our case we created two accessors: for <code>Table</code> and <code>ImmutableTable</code>. There's no handling of inheritance during matching encoding to types, so if we want an encoding to apply both an interface and an immutable implementation (like <code>Table</code> and <code>ImmutableTable</code>), we have to declare all such accessors. The actual names of fields and accessors will follow attribute names in the using class, it's only required that encoding have them unambiguous. The annotation processor then can, more or less safely, extrapolate implementation code like <code>return field;</code> to generate Java source code for accessors in an immutable class.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="c1">// Changing UseTable to use "Table" interface for one of the accessors</span>
<span class="c1">// The encoding will be applied to both.</span>
<span class="nd">@Value.Immutable</span>
<span class="kd">interface</span> <span class="nc">UseTable</span><span class="o"><</span><span class="n">V</span><span class="o">></span> <span class="o">{</span>
<span class="n">ImmutableTable</span><span class="o"><</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="nf">values</span><span class="o">();</span> <span class="c1">// <-- use immutable class</span>
<span class="n">Table</span><span class="o"><</span><span class="n">Integer</span><span class="o">,</span> <span class="n">Integer</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="nf">intValues</span><span class="o">();</span> <span class="c1">// <-- use interface</span>
<span class="o">}</span>
</code></pre></div>
<p>However, that is not yet fully working solution, there's a compilation error in generated code:</p>
<div class="highlight"><pre><code class="language-text" data-lang="text"><span></span>[ERROR] ../sample/encoding-use/target/generated-sources/annotations/uses/ImmutableUseTable.java:[156,35] incompatible types: com.google.common.collect.Table<java.lang.Integer,java.lang.Integer,V> cannot be converted to com.google.common.collect.ImmutableTable<java.lang.Integer,java.lang.Integer,V>
</code></pre></div>
<p>The missing piece is the special routine that initializes <code>ImmutableTable field</code> with the value of <code>Table</code>. This requirement comes from the code that copies object in builder. Having received an instance of <code>UseTable</code> and invoking <code>Table intValues()</code> to get the value, which is then used to initialize in builder <code>ImmutableTable field</code>. While it's possible to craft object to avoid this code to be generated (setting <code>Value.Immutable(copy=false)</code>), we've yet to solve the underlying problem: the need to initialize immutable field from the instance of more general type having unknown implementation. Notice how you would use regular <code>List<T></code> with <em>Immutables</em> processor: you can to initialize attribute values with <code>Iterable<? extends T></code>. We need similar capability to describe the most general type we can convert to <code>ImmutableTable</code>. And we have such!</p>
<p>The annotation <code>@Encoding.Of</code> is used to mark static conversion method. Method is bound to the following restrictions:</p>
<ul>
<li>It must be static and therefore should have the same type parameters as encoding (if there are such).</li>
<li>The return type should match the type of implementation field.</li>
<li>It should have single parameter to accept value. What is important, any <em>exposed</em> accessor type should be assignable to that parameter type, and the processor can generate code which can get from value from a getter and pass to an initializer.</li>
</ul>
<p>For our case <code>Table<? extends R, ? extends C, ? extends V></code> is most general type to accept as initializing value.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="nd">@Encoding</span>
<span class="kd">class</span> <span class="nc">TableEncoding</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="o">{</span>
<span class="nd">@Encoding.Impl</span>
<span class="kd">private</span> <span class="n">ImmutableTable</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="n">value</span><span class="o">;</span>
<span class="nd">@Encoding.Expose</span>
<span class="n">ImmutableTable</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="nf">getImmutableTable</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">value</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Encoding.Expose</span>
<span class="n">Table</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="nf">getTable</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">value</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Encoding.Of</span>
<span class="kd">static</span> <span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="n">ImmutableTable</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="nf">init</span><span class="o">(</span><span class="n">Table</span><span class="o"><?</span> <span class="kd">extends</span> <span class="n">R</span><span class="o">,</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">C</span><span class="o">,</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">V</span><span class="o">></span> <span class="n">table</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">ImmutableTable</span><span class="o">.</span><span class="na">copyOf</span><span class="o">(</span><span class="n">table</span><span class="o">);</span> <span class="c1">// <-- We rely on `copyOf` to cast or defensively copy</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<p>Recompile both modules and watch how the code from our encoding is being "implanted" into the generated code in <code>ImmutableUseTable.java</code>. You can play with adding trivial changes to the way accessors or conversion method are implemented in the encoding and see how implementation code of <code>ImmutableTable</code> changes accordingly.</p>
<h3>Customizing builder</h3>
<p>There's already some geeky stuff happening internally, but nothing interesting so far in terms of convenience and utility that our encoding is called to provide. That's because we haven't got to customizing builder code. And now we are going to describe builder with the encoding. An encoding describes with exemplary code how a single instance ("instantiation") of attribute will be embedded into immutable class. Similarly, a nested builder is used to describe how an attribute is built by providing illustrative fragments of code. When there are no builder declaration in the encoding, the code for the builder is trivially derived from implementation field (or <code>@Encoding.Of</code> conversion method) and requires that attributes would always be initialized using builder. Once encoding builder is defined, it's all up to encoding to control all the aspects of how to build values. Hopefully it's not very complicated to do that.</p>
<p>Start with defining static nested class for a builder part, annotate it with <code>@Encoding.Builder</code> and replicate any type parameters if any (they should be identical to the ones of encoding).</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="nd">@Encoding</span>
<span class="kd">class</span> <span class="nc">TableEncoding</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="o">{</span>
<span class="nd">@Encoding.Impl</span>
<span class="kd">private</span> <span class="n">ImmutableTable</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="n">value</span><span class="o">;</span>
<span class="c1">// ... methods skipped for brevity</span>
<span class="nd">@Encoding.Builder</span> <span class="c1">// <-- put annotation</span>
<span class="kd">static</span> <span class="kd">class</span> <span class="nc">Builder</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="o">{</span> <span class="c1">// <-- copy type parameters from the encoding</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<p>While a good start, we're getting the compilation error:</p>
<div class="highlight"><pre><code class="language-text" data-lang="text"><span></span>[ERROR] ../samples/encoding-def/src/encoding/TableEncoding.java:[28,10] @Encoding.Builder must have no arg method @Encoding.Build. It is used to describe how to get built instance
</code></pre></div>
<p>Here's how to add it:</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="c1">//... only nested builder is shown</span>
<span class="nd">@Encoding.Builder</span>
<span class="kd">static</span> <span class="kd">class</span> <span class="nc">Builder</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="o">{</span>
<span class="nd">@Encoding.Build</span>
<span class="n">ImmutableTable</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="nf">build</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">ImmutableTable</span><span class="o">.</span><span class="na">of</span><span class="o">();</span> <span class="c1">// <-- maybe return empty table on each build?</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<p>That is still not enough, though. The next compilation error still shows missing elements:</p>
<div class="highlight"><pre><code class="language-text" data-lang="text"><span></span>[ERROR] ../samples/encoding-def/src/encoding/TableEncoding.java:[28,10] One of builder init methods should be a copy method, i.e. it should be annotated @Encoding.Init @Encoding.Copy and be able to accept values of type which exposed accessor returns
</code></pre></div>
<p>This is similar to how we defined conversion (<code>@Encoding.Of</code>) method, but now we'll have to do initialization for the builder. Apparently, we are better off creating more complete, realistic builder encoding that would compile and work. Please, follow code comments for extra details.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="c1">//... only nested builder is shown</span>
<span class="nd">@Encoding.Builder</span>
<span class="kd">static</span> <span class="kd">class</span> <span class="nc">Builder</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="o">{</span>
<span class="c1">// we're introducing field to hold intermediate value field</span>
<span class="c1">// And we're even initialize it with default value: empty table</span>
<span class="kd">private</span> <span class="n">ImmutableTable</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="n">buildValue</span> <span class="o">=</span> <span class="n">ImmutableTable</span><span class="o">.</span><span class="na">of</span><span class="o">();</span>
<span class="c1">// This field is nothing special, because you can have as many</span>
<span class="c1">// helper builder fields per attribute as you want and their names</span>
<span class="c1">// just have to be unambiguous, no patterns special to follow.</span>
<span class="nd">@Encoding.Init</span> <span class="c1">// <-- specify builder initializer method</span>
<span class="nd">@Encoding.Copy</span> <span class="c1">// <-- marks it as "canonical" copy method</span>
<span class="c1">// For copy init methods, the name of a method is irrelevant</span>
<span class="c1">// as generated methods are following a name of a corresponding attribute</span>
<span class="c1">// and naming styles applied elsewhere</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">set</span><span class="o">(</span><span class="n">Table</span><span class="o"><?</span> <span class="kd">extends</span> <span class="n">R</span><span class="o">,</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">C</span><span class="o">,</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">V</span><span class="o">></span> <span class="n">table</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// As in the case with conversion method, we accept more general type</span>
<span class="c1">// and safely copy it to our field</span>
<span class="c1">// if you would restrict null values it is better to do it here to fail</span>
<span class="c1">// fast, but in our case `ImmutableTable.copyOf` takes care of everything.</span>
<span class="k">this</span><span class="o">.</span><span class="na">buildValue</span> <span class="o">=</span> <span class="n">ImmutableTable</span><span class="o">.</span><span class="na">copyOf</span><span class="o">(</span><span class="n">table</span><span class="o">);</span>
<span class="c1">// please note, that we don't have to `return this;` like we usually do</span>
<span class="c1">// in builder initializers. Here we have just a void method, but generated</span>
<span class="c1">// initializers will actually return builder for chained invocation,</span>
<span class="c1">// so this is covered.</span>
<span class="o">}</span>
<span class="nd">@Encoding.Build</span> <span class="c1">// <-- marks build finalization method</span>
<span class="c1">// the method name is irrelevant</span>
<span class="c1">// the return value should match implementation field type</span>
<span class="n">ImmutableTable</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="nf">build</span><span class="o">()</span> <span class="o">{</span>
<span class="c1">// We return whatever we have as of now.</span>
<span class="c1">// buildValue field was initialized with empty table and</span>
<span class="c1">// can be only reassigned to proper ImmutableTable value</span>
<span class="c1">// so we don't check anything here. But if we would like to check for null</span>
<span class="c1">// or other invariants, we would do this here and throw IllegalStateException</span>
<span class="c1">// explaining why attribute value cannot be build.</span>
<span class="k">return</span> <span class="n">buildValue</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<p>Such encoding will compile and work. The annotation processor will generate builder code which behave almost as the default code, but with one difference: as we've initialized builder field with empty table, build method will not complain if call to "set" initializer was omitted during construction, there attributes value will be empty table unless initialized to some other value. While another uninteresting example, I believe it was necessary to demonstrate very basic structure the builder might have and provide explaining comments.</p>
<p>Of course, a builder for our <code>TableEncoding</code> should have convenience methods to build <code>ImmutableTable</code> and with the next attempt we'll cover this by using <code>ImmutableTable.Builder</code> as a implementation helper. Please, follow code comments for extra details.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="c1">//... only nested builder is shown</span>
<span class="nd">@Encoding.Builder</span>
<span class="kd">static</span> <span class="kd">class</span> <span class="nc">Builder</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="o">{</span>
<span class="c1">// holding internal builder</span>
<span class="kd">private</span> <span class="n">ImmutableTable</span><span class="o">.</span><span class="na">Builder</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="n">builder</span> <span class="o">=</span> <span class="n">ImmutableTable</span><span class="o">.<</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span><span class="n">builder</span><span class="o">();</span>
<span class="nd">@Encoding.Init</span> <span class="c1">// defines additional initializer method</span>
<span class="c1">// the method name matters here as it would became prefix of</span>
<span class="c1">// the generated initializer method, for an attribute named 'foo',</span>
<span class="c1">// a generated will be named 'putFoo'</span>
<span class="kt">void</span> <span class="nf">put</span><span class="o">(</span><span class="n">R</span> <span class="n">row</span><span class="o">,</span> <span class="n">C</span> <span class="n">column</span><span class="o">,</span> <span class="n">V</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// here, table builder handles checks for us</span>
<span class="n">builder</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">row</span><span class="o">,</span> <span class="n">column</span><span class="o">,</span> <span class="n">value</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@Encoding.Init</span> <span class="c1">// defines additional initializer method</span>
<span class="c1">// for an attribute named 'bar', a generated initializer will be named 'putAllBar'</span>
<span class="kt">void</span> <span class="nf">putAll</span><span class="o">(</span><span class="n">Table</span><span class="o"><?</span> <span class="kd">extends</span> <span class="n">R</span><span class="o">,</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">C</span><span class="o">,</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">V</span><span class="o">></span> <span class="n">table</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// here, table builder handles all checks for us</span>
<span class="n">builder</span><span class="o">.</span><span class="na">putAll</span><span class="o">(</span><span class="n">table</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@Encoding.Init</span>
<span class="nd">@Encoding.Copy</span> <span class="c1">// canonical copy-initializer, sets/overwrites table</span>
<span class="c1">// for init-copy initializers, generated method name is derived</span>
<span class="c1">// from attribute name and current style</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">set</span><span class="o">(</span><span class="n">Table</span><span class="o"><?</span> <span class="kd">extends</span> <span class="n">R</span><span class="o">,</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">C</span><span class="o">,</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">V</span><span class="o">></span> <span class="n">table</span><span class="o">)</span> <span class="o">{</span>
<span class="c1">// reassigning builder as set supposed to</span>
<span class="n">builder</span> <span class="o">=</span> <span class="n">ImmutableTable</span><span class="o">.<</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span><span class="n">builder</span><span class="o">().</span><span class="na">putAll</span><span class="o">(</span><span class="n">table</span><span class="o">);</span>
<span class="o">}</span>
<span class="nd">@Encoding.Build</span>
<span class="n">ImmutableTable</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="nf">build</span><span class="o">()</span> <span class="o">{</span>
<span class="c1">// this is straightforward</span>
<span class="c1">// just build table from whatever we have accumulated in builder</span>
<span class="k">return</span> <span class="n">builder</span><span class="o">.</span><span class="na">build</span><span class="o">();</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<h2>How it works</h2>
<p>(Magic) ...TBD</p>
<h2>How To...</h2>
<p><a name="meta-annotations"></a></p>
<h3>Enabling encoding via meta-annotations</h3>
<p>The activation annotation can be used also as meta-annotation: imagine having special "stereotype" annotation which is itself annotated with <code>*Enabled</code> annotations as well as any relevant <code>Value.Style</code> annotation. All in all, placing encoding activation annotation follows the same rules as <a href="style.html#apply-style">applying styles</a></p>
<h3>Adding helper methods</h3>
<p>You can add public (or package-private which will work the same here), private and static helper methods. Public methods will be exposed per attribute. Private methods will be used only internally.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="c1">// isEmptyAttr would be generated for every table attribute</span>
<span class="kt">boolean</span> <span class="nf">isEmpty</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">value</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">();</span>
<span class="o">}</span>
</code></pre></div>
<h3>Customize naming</h3>
<p>For most elements, naming patterns will be derived automatically either assumed by their role or by using method name in encoding as a prefix. But you can override naming pattens and set depluralization hint where needed. Use <code>@Encoding.Naming</code> annotation for that. Errors/Warnings will be reported if misused. Use <code>StandardNaming</code> enum values where applicable, so downstream encoding users can use usual <code>@Value.Style</code> customization attributes which will be applicable to naming.</p>
<p>Here's example of putting annotations on table builder methods. Fields and method implementations are left out for brevity.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="o">...</span>
<span class="nd">@Encoding.Builder</span>
<span class="kd">static</span> <span class="kd">class</span> <span class="nc">Builder</span><span class="o"><</span><span class="n">R</span><span class="o">,</span> <span class="n">C</span><span class="o">,</span> <span class="n">V</span><span class="o">></span> <span class="o">{</span>
<span class="nd">@Encoding.Init</span>
<span class="nd">@Encoding.Naming</span><span class="o">(</span><span class="n">standard</span> <span class="o">=</span> <span class="n">StandardNaming</span><span class="o">.</span><span class="na">PUT</span><span class="o">)</span> <span class="c1">// standard "putAttr"</span>
<span class="kt">void</span> <span class="nf">put</span><span class="o">(</span><span class="n">R</span> <span class="n">row</span><span class="o">,</span> <span class="n">C</span> <span class="n">column</span><span class="o">,</span> <span class="n">V</span> <span class="n">value</span><span class="o">)</span> <span class="o">{...}</span>
<span class="nd">@Encoding.Init</span>
<span class="nd">@Encoding.Naming</span><span class="o">(</span><span class="n">standard</span> <span class="o">=</span> <span class="n">StandardNaming</span><span class="o">.</span><span class="na">PUT_ALL</span><span class="o">)</span> <span class="c1">// standard "putAllAttr"</span>
<span class="kt">void</span> <span class="nf">putAll</span><span class="o">(</span><span class="n">Table</span><span class="o"><?</span> <span class="kd">extends</span> <span class="n">R</span><span class="o">,</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">C</span><span class="o">,</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">V</span><span class="o">></span> <span class="n">table</span><span class="o">)</span> <span class="o">{...}</span>
<span class="nd">@Encoding.Init</span>
<span class="nd">@Encoding.Copy</span>
<span class="nd">@Encoding.Naming</span><span class="o">(</span><span class="s">"reset*"</span><span class="o">)</span> <span class="c1">// will result in "resetAttr", not customizable with styles</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">set</span><span class="o">(</span><span class="n">Table</span><span class="o"><?</span> <span class="kd">extends</span> <span class="n">R</span><span class="o">,</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">C</span><span class="o">,</span> <span class="o">?</span> <span class="kd">extends</span> <span class="n">V</span><span class="o">></span> <span class="n">table</span><span class="o">)</span> <span class="o">{...}</span>
<span class="o">}</span>
</code></pre></div>
<h3>Customize with methods</h3>
<p>Encodings provide the way to "encode" <code>with*</code> methods with custom signatures. There's Javadoc on <code>@Encoding.Copy</code> and error messages if misused.</p>
<p>Here is example of custom with methods for hypothetical <code>Option</code> encoding. Builder encoding is left out for brevity.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="nd">@Encoding</span>
<span class="kd">class</span> <span class="nc">OptionEncoding</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="o">{</span>
<span class="nd">@Encoding.Impl</span>
<span class="kd">private</span> <span class="n">Option</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="n">field</span> <span class="o">=</span> <span class="n">Option</span><span class="o">.</span><span class="na">none</span><span class="o">();</span>
<span class="c1">// if you specify one of the copy methods, the default one is not longer generated</span>
<span class="c1">// so you need to declare both alternative methods</span>
<span class="nd">@Encoding.Copy</span>
<span class="kd">public</span> <span class="n">Option</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="nf">withOption</span><span class="o">(</span><span class="n">Option</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">Objects</span><span class="o">.</span><span class="na">requireNonNull</span><span class="o">(</span><span class="n">value</span><span class="o">);</span> <span class="c1">// insert any checks necessary</span>
<span class="o">}</span>
<span class="nd">@Encoding.Copy</span>
<span class="kd">public</span> <span class="n">Option</span><span class="o"><</span><span class="n">T</span><span class="o">></span> <span class="nf">with</span><span class="o">(</span><span class="n">T</span> <span class="n">value</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">Option</span><span class="o">.</span><span class="na">some</span><span class="o">(</span><span class="n">value</span><span class="o">);</span> <span class="c1">// insert any checks necessary</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<h3>Getting attribute name</h3>
<p>If you need attribute name as a string value inside encoding use asterisk in angle brackets inside string literal: <code>"<*>"</code>. This placeholder will be replaced in compile time with current attribute name. This can be used to generate exception messages and creating attribute related constant values.</p>
<h3>Virtual fields</h3>
<p>Implementation fields can be marked as virtual to allow alternative internal storage of the value by using one or more other fields. But, what is important is that value should be still converted to (if conversion method defined) and smuggled in constructor as a single <code>@Encoding.Impl</code> value.</p>
<div class="highlight"><pre><code class="language-java" data-lang="java"><span></span><span class="c1">// This encoding is rudimentary/incomplete and serves only as example.</span>
<span class="nd">@Encoding</span>
<span class="kd">class</span> <span class="nc">CompactOptionalDouble</span> <span class="o">{</span>
<span class="nd">@Encoding.Impl</span><span class="o">(</span><span class="n">virtual</span> <span class="o">=</span> <span class="kc">true</span><span class="o">)</span>
<span class="kd">private</span> <span class="n">OptionalDouble</span> <span class="n">opt</span><span class="o">;</span> <span class="c1">// will not be stored as a field</span>
<span class="c1">// but these derived values will be stored as object fields.</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="kt">double</span> <span class="n">value</span> <span class="o">=</span> <span class="n">opt</span><span class="o">.</span><span class="na">orElse</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
<span class="kd">private</span> <span class="kd">final</span> <span class="kt">boolean</span> <span class="n">present</span> <span class="o">=</span> <span class="n">opt</span><span class="o">.</span><span class="na">isPresent</span><span class="o">();</span>
<span class="nd">@Encoding.Expose</span>
<span class="n">OptionalDouble</span> <span class="nf">get</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">present</span>
<span class="o">?</span> <span class="n">OptionalDouble</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">value</span><span class="o">)</span>
<span class="o">:</span> <span class="n">OptionalDouble</span><span class="o">.</span><span class="na">empty</span><span class="o">();</span>
<span class="o">}</span>
<span class="c1">// Custom helper accessors can bypass OptionalDouble creation</span>
<span class="nd">@Encoding.Naming</span><span class="o">(</span><span class="s">"is*Present"</span><span class="o">)</span>
<span class="kt">boolean</span> <span class="nf">isPresent</span><span class="o">()</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">present</span><span class="o">;</span>
<span class="o">}</span>
<span class="nd">@Encoding.Naming</span><span class="o">(</span><span class="s">"*OrElse"</span><span class="o">)</span>
<span class="kt">double</span> <span class="nf">orElse</span><span class="o">(</span><span class="kt">double</span> <span class="n">defaultValue</span><span class="o">)</span> <span class="o">{</span>
<span class="k">return</span> <span class="n">present</span> <span class="o">?</span> <span class="n">value</span> <span class="o">:</span> <span class="n">defaultValue</span><span class="o">;</span>
<span class="o">}</span>
<span class="o">}</span>
</code></pre></div>
<h2>Limitations</h2>
<h3>Annotations are not supported yet as encoding qualifiers</h3>
<h3>Parser/processor limitations (method references)</h3>
</section>
<footer class="jumbotron">
<div class="container">
<h2>Guides</h2>
<ul>
<li><a href="/getstarted.html">Get started!</a></li>
<li><a href="/intro.html">Inception</a></li>
<li><a href="/immutable.html">Immutable objects</a></li>
<li><a href="/factory.html">Factory builders</a></li>
<li><a href="/functional.html">Functions and Predicates (for Java 7)</a></li>
<li><a href="/style.html">Style customization</a></li>
<li><a href="/json.html">JSON serialization</a></li>
<li><a href="/mongo.html">MongoDB repositories</a></li>
<li><a href="/encoding.html">Encoding: Customizing attributes and builders (experimental)</a></li>
<li><a href="/apt.html">Using annotation processor in IDE</a></li>
</ul>
<h2>Get involved</h2>
<ul>
<li>Clone source repository, contribute bug reports and fixes on <a href="https://github.com/immutables/immutables">GitHub immutables/immutables</a></li>
<li>News and announcements on twitter <a href="https://twitter.com/ImmutablesOrg">@ImmutablesOrg</a></li>
<li>Ask questions or give feedback and ideas using mailing group <a href="https://groups.google.com/forum/#!forum/immutables">immutables at googlegroups.com</a></li>
</ul>
<p><a href="/license.html">Apache License 2.0</a></p>
<!--<div><h2>Posts</h2>
<ul>
</ul>
</div>-->
</div>
</footer>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<script defer src="/gfx/jquery.toc.min.js"></script>
<script>
$(function() {
$('#toc').toc({
container: '.documentation',
selectors: 'h1,h2,h3,h4',
anchorName: function(i, heading, prefix) {
heading = $(heading).text();
if (heading.trim) heading = heading.trim();
return heading.toLowerCase().replace(/ /g, '-').replace(/[^a-z^\-]+/g, '');
},
})
})
</script>
<script async defer id="github-bjs" src="https://buttons.github.io/buttons.js"></script>
</body>
</html>