-
Notifications
You must be signed in to change notification settings - Fork 0
/
chapter-13.txt
1035 lines (562 loc) · 62.2 KB
/
chapter-13.txt
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
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
Chapter 13. Binary Compatibility
Table of Contents
13.1. The Form of a Binary
13.2. What Binary Compatibility Is and Is Not
13.3. Evolution of Packages and Modules
13.4. Evolution of Classes
13.4.1. abstract Classes
13.4.2. sealed, non-sealed, and final Classes
13.4.2.1. sealed Classes
13.4.2.2. non-sealed Classes
13.4.2.3. final Classes
13.4.3. public Classes
13.4.4. Superclasses and Superinterfaces
13.4.5. Class Type Parameters
13.4.6. Class Body and Member Declarations
13.4.7. Access to Members and Constructors
13.4.8. Field Declarations
13.4.9. final Fields and static Constant Variables
13.4.10. static Fields
13.4.11. transient Fields
13.4.12. Method and Constructor Declarations
13.4.13. Method and Constructor Type Parameters
13.4.14. Method and Constructor Formal Parameters
13.4.15. Method Result Type
13.4.16. abstract Methods
13.4.17. final Methods
13.4.18. native Methods
13.4.19. static Methods
13.4.20. synchronized Methods
13.4.21. Method and Constructor Throws
13.4.22. Method and Constructor Body
13.4.23. Method and Constructor Overloading
13.4.24. Method Overriding
13.4.25. Static Initializers
13.4.26. Evolution of Enum Classes
13.4.27. Evolution of Record Classes
13.5. Evolution of Interfaces
13.5.1. public Interfaces
13.5.2. sealed and non-sealed Interfaces
13.5.3. Superinterfaces
13.5.4. Interface Members
13.5.5. Interface Type Parameters
13.5.6. Field Declarations
13.5.7. Interface Method Declarations
13.5.8. Annotation Interfaces
Development tools for the Java programming language should support automatic recompilation as necessary whenever source code is available. Particular implementations may also store the source and binary representations of classes and interfaces in a versioning database and implement a ClassLoader that uses integrity mechanisms of the database to prevent linkage errors by providing binary-compatible versions of classes and interfaces to clients.
Developers of packages, classes, and interfaces that are to be widely distributed face a different set of problems. In the Internet, which is our favorite example of a widely distributed system, it is often impractical or impossible to automatically recompile the pre-existing binaries that directly or indirectly depend on a class or interface that is to be changed. Instead, this specification defines a set of changes that developers are permitted to make to a package or to a class or interface while preserving (not breaking) compatibility with pre-existing binaries.
Within the framework of Release-to-Release Binary Compatibility in SOM (Forman, Conner, Danforth, and Raper, Proceedings of OOPSLA '95), Java programming language binaries are binary compatible under all relevant transformations that the authors identify (with some caveats with respect to the addition of instance variables). Using their scheme, here is a list of some important binary compatible changes that the Java programming language supports:
Reimplementing existing methods, constructors, and initializers to improve performance.
Changing methods or constructors to return values on inputs for which they previously either threw exceptions that normally should not occur or failed by going into an infinite loop or causing a deadlock.
Adding new fields, methods, or constructors to an existing class or interface.
Deleting private fields, methods, or constructors of a class.
When an entire package is updated, deleting package access fields, methods, or constructors of classes and interfaces in the package.
Reordering the fields, methods, or constructors in an existing class or interface declaration.
Moving a method upward in the class hierarchy.
Reordering the list of direct superinterfaces of a class or interface.
Inserting new class or interface types in the type hierarchy.
This chapter specifies minimum standards for binary compatibility guaranteed by all implementations. The Java programming language guarantees compatibility when binaries of classes and interfaces are mixed that are not known to be from compatible sources, but whose sources have been modified in the compatible ways described here. Note that we are discussing compatibility between releases of an application. A discussion of compatibility among releases of the Java SE Platform is beyond the scope of this chapter.
We encourage development systems to provide facilities that alert developers to the impact of changes on pre-existing binaries that cannot be recompiled.
This chapter first specifies some properties that any binary format for the Java programming language must have (§13.1). It next defines binary compatibility, explaining what it is and what it is not (§13.2). It finally enumerates a large set of possible changes to packages (§13.3), classes (§13.4), and interfaces (§13.5), specifying which of these changes are guaranteed to preserve binary compatibility and which are not.
13.1. The Form of a Binary
Programs must be compiled either into the class file format specified by The Java Virtual Machine Specification, Java SE 23 Edition, or into a representation that can be mapped into that format by a class loader written in the Java programming language.
A class file corresponding to a class or interface declaration must have certain properties. A number of these properties are specifically chosen to support source code transformations that preserve binary compatibility. The required properties are:
The class or interface must be named by its binary name, which must meet the following constraints:
The binary name of a top level class or interface (§7.6) is its canonical name (§6.7).
The binary name of a member class or interface (§8.5, §9.5) consists of the binary name of its immediately enclosing class or interface, followed by $, followed by the simple name of the member.
The binary name of a local class or interface (§14.3) consists of the binary name of its immediately enclosing class or interface, followed by $, followed by a non-empty sequence of digits, followed by the simple name of the local class.
The binary name of an anonymous class (§15.9.5) consists of the binary name of its immediately enclosing class or interface, followed by $, followed by a non-empty sequence of digits.
The binary name of a type variable declared by a generic class or interface (§8.1.2, §9.1.2) is the binary name of its immediately enclosing class or interface, followed by $, followed by the simple name of the type variable.
The binary name of a type variable declared by a generic method (§8.4.4) is the binary name of the class or interface declaring the method, followed by $, followed by the descriptor of the method (JVMS §4.3.3), followed by $, followed by the simple name of the type variable.
The binary name of a type variable declared by a generic constructor (§8.8.4) is the binary name of the class declaring the constructor, followed by $, followed by the descriptor of the constructor (JVMS §4.3.3), followed by $, followed by the simple name of the type variable.
A reference to another class or interface must be symbolic, using the binary name of the class or interface.
A reference to a field that is a constant variable (§4.12.4) must be resolved at compile time to the value V denoted by the constant variable's initializer.
If such a field is static, then no reference to the field should be present in the code in a binary file, including the class or interface which declared the field. Such a field must always appear to have been initialized (§12.4.2); the default initial value for the field (if different than V) must never be observed.
If such a field is non-static, then no reference to the field should be present in the code in a binary file, except in the class containing the field. (It will be a class rather than an interface, since an interface has only static fields.) The class should have code to set the field's value to V during instance creation (§12.5).
Given a legal expression denoting a field access in a class C, referencing a field named f that is not a constant variable and is declared in a (possibly distinct) class or interface D, we define the qualifying class or interface of the field reference as follows:
If the expression is referenced by a simple name, then if f is a member of the current class or interface, C, then let Q be C. Otherwise, let Q be the innermost lexically enclosing class or interface declaration of which f is a member. In either case, Q is the qualifying class or interface of the reference.
If the reference is of the form TypeName.f, where TypeName denotes a class or interface, then the class or interface denoted by TypeName is the qualifying class or interface of the reference.
If the expression is of the form ExpressionName.f or Primary.f, then:
If the compile-time type of ExpressionName or Primary is an intersection type V1 & ... & Vn (§4.9), then the qualifying class or interface of the reference is the erasure (§4.6) of V1.
Otherwise, the erasure of the compile-time type of ExpressionName or Primary is the qualifying class or interface of the reference.
If the expression is of the form super.f, then the superclass of C is the qualifying class or interface of the reference.
If the expression is of the form TypeName.super.f, then the superclass of the class denoted by TypeName is the qualifying class or interface of the reference.
The reference to f must be compiled into a symbolic reference to the qualifying class or interface of the reference, plus the simple name of the field, f.
The reference must also include a symbolic reference to the erasure of the declared type of the field, so that the verifier can check that the type is as expected.
Given a method invocation expression or a method reference expression in a class or interface C, referencing a method named m declared (or implicitly declared (§9.2)) in a (possibly distinct) class or interface D, we define the qualifying class or interface of the method invocation as follows:
If D is Object then the qualifying class or interface of the method invocation is Object.
Otherwise:
If the method is referenced by a simple name, then if m is a member of the current class or interface C, let Q be C; otherwise, let Q be the innermost lexically enclosing class or interface declaration of which m is a member. In either case, Q is the qualifying class or interface of the method invocation.
If the expression is of the form TypeName.m or ReferenceType::m, then the class or interface denoted by TypeName, or the erasure of ReferenceType, is the qualifying class or interface of the method invocation.
If the expression is of the form ExpressionName.m or Primary.m or ExpressionName::m or Primary::m, then:
If the compile-time type of ExpressionName or Primary is an intersection type V1 & ... & Vn, then the qualifying class or interface of the method invocation is the erasure of V1.
Otherwise, the erasure of the compile-time type of ExpressionName or Primary is the qualifying class or interface of the method invocation.
If the expression is of the form super.m or super::m, then the superclass of C is the qualifying class or interface of the method invocation.
If the expression is of the form TypeName.super.m or TypeName.super::m, then if TypeName denotes a class X, the superclass of X is the qualifying class or interface of the method invocation; if TypeName denotes an interface X, X is the qualifying class or interface of the method invocation.
A reference to a method must be resolved at compile time to a symbolic reference to the qualifying class or interface of the method invocation, plus the erasure of the declared signature (§8.4.2) of the method. The signature of a method must include all of the following as determined by §15.12.3:
The simple name of the method
The number of parameters to the method
A symbolic reference to the type of each parameter
A reference to a method must also include either a symbolic reference to the erasure of the return type of the denoted method or an indication that the denoted method is declared void and does not return a value.
Given a class instance creation expression (§15.9) or an explicit constructor invocation statement (§8.8.7.1) or a method reference expression of the form ClassType :: new (§15.13) in a class or interface C, referencing a constructor m declared in a (possibly distinct) class or interface D, we define the qualifying class of the constructor invocation as follows:
If the expression is of the form new D(...) or ExpressionName.new D(...) or Primary.new D(...) or D :: new, then the qualifying class of the constructor invocation is D.
If the expression is of the form new D(...){...} or ExpressionName.new D(...){...} or Primary.new D(...){...}, then the qualifying class of the constructor invocation is the anonymous class declared by the expression.
If the expression is of the form super(...) or ExpressionName.super(...) or Primary.super(...), then the qualifying class of the constructor invocation is the direct superclass of C.
If the expression is of the form this(...), then the qualifying class of the constructor invocation is C.
A reference to a constructor must be resolved at compile time to a symbolic reference to the qualifying class of the constructor invocation, plus the declared signature of the constructor (§8.8.2). The signature of a constructor must include both:
The number of parameters of the constructor
A symbolic reference to the type of each formal parameter
A binary representation for a class or interface must also contain all of the following:
If it is a class and is not Object, then a symbolic reference to the direct superclass of this class.
A symbolic reference to each direct superinterface, if any.
A specification of each field declared in the class or interface, given as the simple name of the field and a symbolic reference to the erasure of the type of the field.
If it is a class, then the erased signature of each constructor, as described above.
For each method declared in the class or interface (excluding, for an interface, its implicitly declared methods (§9.2)), its erased signature and return type, as described above.
The code needed to implement the class or interface:
For an interface, code for the field initializers and the implementation of each method with a block body (§9.4.3).
For a class, code for the field initializers, the instance and static initializers, the implementation of each method with a block body (§8.4.7), and the implementation of each constructor.
Every class or interface must contain sufficient information to recover its canonical name (§6.7).
Every member class or interface must have sufficient information to recover its source-level access modifier (§6.6).
Every nested class or interface must have a symbolic reference to its immediately enclosing class or interface (§8.1.3).
Every class or interface must contain symbolic references to all of its member classes and interfaces (§8.5, §9.5), and to all other nested classes and interfaces declared within its body.
A construct emitted by a Java compiler must be marked as synthetic if it does not correspond to a construct declared explicitly or implicitly in source code, unless the emitted construct is a class initialization method (JVMS §2.9).
A construct emitted by a Java compiler must be marked as mandated if it corresponds to a formal parameter declared implicitly in source code (§8.8.1, §8.8.9, §8.9.3, §15.9.5.1).
The following formal parameters are declared implicitly in source code:
The first formal parameter of a constructor of a non-private inner member class (§8.8.1, §8.8.9).
The first formal parameter of an anonymous constructor of an anonymous class whose superclass is an inner class (not in a static context) (§15.9.5.1).
The formal parameter name of the valueOf method which is implicitly declared in an enum class (§8.9.3).
The formal parameters of a compact constructor of a record class (§8.10.4).
For reference, the following constructs are declared implicitly in source code, but are not marked as mandated because only formal parameters and modules can be so marked in a class file (JVMS §4.7.24, JVMS §4.7.25):
Default constructors of normal and enum classes (§8.8.9, §8.9.2)
Canonical constructors of record classes (§8.10.4)
Anonymous constructors (§15.9.5.1)
The values and valueOf methods of enum classes (§8.9.3)
Certain public fields of enum classes (§8.9.3)
Certain private fields and public methods of record classes (§8.10.3)
Certain public methods of interfaces (§9.2)
Container annotations (§9.7.5)
A class file corresponding to a module declaration must have the properties of a class file for a class whose binary name is module-info and which has no superclass, no superinterfaces, no fields, and no methods. In addition, the binary representation of the module must contain all of the following:
A specification of the name of the module, given as a symbolic reference to the name indicated after module. Also, the specification must include whether the module is normal or open (§7.7).
A specification of each dependence denoted by a requires directive, given as a symbolic reference to the name of the module indicated by the directive (§7.7.1). Also, the specification must include whether the dependence is transitive and whether the dependence is static.
A specification of each package denoted by an exports or opens directive, given as a symbolic reference to the name of the package indicated by the directive (§7.7.2). Also, if the directive was qualified, the specification must give symbolic references to the names of the modules indicated by the directive's to clause.
A specification of each service denoted by a uses directive, given as a symbolic reference to the name of the class or interface indicated by the directive (§7.7.3).
A specification of the service providers denoted by a provides directive, given as symbolic references to the names of the classes and interfaces indicated by the directive's with clause (§7.7.4). Also, the specification must give a symbolic reference to the name of the class or interface indicated as the service by the directive.
The following sections discuss changes that may be made to class and interface declarations without breaking compatibility with pre-existing binaries. Under the translation requirements given above, the Java Virtual Machine and its class file format support these changes. Any other valid binary format, such as a compressed or encrypted representation that is mapped back into class files by a class loader under the above requirements, will necessarily support these changes as well.
13.2. What Binary Compatibility Is and Is Not
A change to a type is binary compatible with (equivalently, does not break binary compatibility with) pre-existing binaries if pre-existing binaries that previously linked without error will continue to link without error.
Binaries are compiled to rely on the accessible members and constructors of other classes and interfaces. To preserve binary compatibility, a class or interface should treat its accessible members and constructors, their existence and behavior, as a contract with its users.
The Java programming language is designed to prevent additions to contracts and accidental name collisions from breaking binary compatibility. Specifically, addition of more methods overloading a particular method name does not break compatibility with pre-existing binaries. The method signature that the pre-existing binary will use for method lookup is chosen by the overload resolution algorithm at compile time (§15.12.2).
If the Java programming language had been designed so that the particular method to be executed was chosen at run time, then such an ambiguity might be detected at run time. Such a rule would imply that adding an additional overloaded method so as to make ambiguity possible at a call site could break compatibility with an unknown number of pre-existing binaries. See §13.4.23 for more discussion.
Binary compatibility is not the same as source compatibility. In particular, the example in §13.4.6 shows that a set of compatible binaries can be produced from sources that will not compile all together. This example is typical: a new declaration is added, changing the meaning of a name in an unchanged part of the source code, while the pre-existing binary for that unchanged part of the source code retains the fully-qualified, previous meaning of the name. Producing a consistent set of source code requires providing a qualified name or field access expression corresponding to the previous meaning.
13.3. Evolution of Packages and Modules
A new top level class or interface may be added to a package without breaking compatibility with pre-existing binaries, provided the new class or interface does not reuse a name previously given to an unrelated class or interface. If a new class or interface reuses a name previously given to an unrelated class or interface, then a conflict may result, since binaries for both classes or interfaces could not be loaded by the same class loader.
Changes in top level classes and interfaces that are not public and that are not a superclass or superinterface, respectively, of a public class or interface, affect only classes and interfaces within the package in which they are declared. Such classes and interfaces may be deleted or otherwise changed, even if incompatibilities are otherwise described here, provided that the affected binaries of that package are updated together.
If a module that was declared to export or open a package is changed to not export or open the package, or to export or open the package to a different set of friends, then an IllegalAccessError is thrown if a pre-existing binary is linked that needs but no longer has access to the public and protected classes and interfaces of the package. Such a change is not recommended for modules that have been widely distributed.
If a module was not declared to export or open a given package, then changing the module to export or open the package does not break compatibility with pre-existing binaries. However, changing the module to export the package may prevent the program from starting, since any module that reads the module may also read some other module that exports a package with the same name.
Adding a requires directive to a module declaration, or adding the transitive modifier to a requires directive, does not break compatibility with pre-existing binaries. However, it may prevent the program from starting, since the module may now read multiple modules that export packages with the same name.
Deleting a requires directive in a module declaration, or deleting the transitive modifier from a requires directive, may break compatibility with any pre-existing binary that relied on the directive or modifier for readability of a given module in the course of referencing classes and interfaces exported by that module. An IllegalAccessError may be thrown when such a reference from a pre-existing binary is linked.
Adding or deleting a uses or provides directive in a module declaration does not break compatibility with pre-existing binaries.
13.4. Evolution of Classes
This section describes the effects of changes to the declaration of a class and its members and constructors on pre-existing binaries.
13.4.1. abstract Classes
If a class that was not declared abstract is changed to be declared abstract, then pre-existing binaries that attempt to create new instances of that class will throw either an InstantiationError at link time, or (if a reflective method is used) an InstantiationException at run time; such a change is therefore not recommended for widely distributed classes.
Changing a class that is declared abstract to no longer be declared abstract does not break compatibility with pre-existing binaries.
13.4.2. sealed, non-sealed, and final Classes
13.4.2.1. sealed Classes
If a class that was freely extensible (§8.1.1.2) is changed to be declared sealed, then an IncompatibleClassChangeError is thrown if a binary of a pre-existing subclass of this class is loaded and is not a permitted direct subclass of this class (§8.1.6); such a change is not recommended for widely distributed classes.
Changing a class that was declared final to be declared sealed does not break compatibility with pre-existing binaries.
Adding a class to the set of permitted direct subclasses of a sealed class will not break compatibility with pre-existing binaries.
Note that evolving a sealed class by adding a permitted direct subclass is considered a binary compatible change because pre-existing binaries that previously linked without error (e.g., a class file that contains an exhaustive switch (§14.11.1)) will continue to link without error. A class file that contains an exhaustive switch will not fail to link if the sealed class that it switches over is expanded by the hierarchy's owner to have a new permitted direct subclass. The JVM is not required to perform exhaustiveness checks when linking a class file that contains an exhaustive switch.
The execution of an exhaustive switch can fail with an error (a MatchException is thrown) if it encounters an instance of a permitted direct subclass that was not known at compile time (§14.11.3, §15.28.2). Strictly speaking, the error is not flagging a binary incompatible change of the sealed class, but more accurately a migration incompatible change of the sealed class.
If a class is removed from the set of permitted direct subclasses of a sealed class, then an IncompatibleClassChangeError is thrown if the pre-existing binary of the removed class is loaded.
Deleting the sealed modifier from a class that does not have a sealed direct superclass or a sealed direct superinterface does not break compatibility with pre-existing binaries.
If a sealed class C did have a sealed direct superclass or a sealed direct superinterface, then deleting the sealed modifier would prevent C from being recompiled, as every class with a sealed direct superclass or a sealed direct superinterface must be either final, sealed, or non-sealed.
13.4.2.2. non-sealed Classes
Changing a class that was declared sealed to be declared non-sealed does not break compatibility with pre-existing binaries.
Changing a class that was declared final to be declared non-sealed does not break compatibility with pre-existing binaries.
A non-sealed class C must have a sealed direct superclass or a sealed direct superinterface (§8.1.1.2). Deleting the non-sealed modifier would prevent C from being recompiled, as every class with a sealed direct superclass or a sealed direct superinterface must be either final, sealed, or non-sealed.
13.4.2.3. final Classes
If a class that was not declared final is changed to be declared final, then an IncompatibleClassChangeError is thrown if a binary of a pre-existing subclass of this class is loaded, because final classes can have no subclasses; such a change is not recommended for widely distributed classes.
Deleting the final modifier from a class that does not have a sealed direct superclass or a sealed direct superinterface does not break compatibility with pre-existing binaries.
If a final class C did have a sealed direct superclass or a sealed direct superinterface, then deleting the final modifier would prevent C from being recompiled, as every class with a sealed direct superclass or a sealed direct superinterface must be either final, sealed, or non-sealed (§8.1.1.2).
13.4.3. public Classes
Changing a class that is not declared public to be declared public does not break compatibility with pre-existing binaries.
If a class that was declared public is changed to not be declared public, then an IllegalAccessError is thrown if a pre-existing binary is linked that needs but no longer has access to the class type; such a change is not recommended for widely distributed classes.
13.4.4. Superclasses and Superinterfaces
A ClassCircularityError is thrown at load time if a class would be a superclass of itself. Changes to the class hierarchy that could result in such a circularity when newly compiled binaries are loaded with pre-existing binaries are not recommended for widely distributed classes.
Changing the direct superclass type or the set of direct superinterface types of a class will not break compatibility with pre-existing binaries, provided that the total set of superclasses or superinterfaces, respectively, of the class loses no members.
For example, it is binary compatible to replace a raw supertype of a class with a parameterization of the class or interface named by the raw type.
If a change to the direct superclass or the set of direct superinterfaces results in any class or interface no longer being a superclass or superinterface, respectively, then linkage errors may result if pre-existing binaries are loaded with the binary of the modified class. Such changes are not recommended for widely distributed classes.
Example 13.4.4-1. Changing A Superclass
Suppose that the following test program:
class Hyper { char h = 'h'; }
class Super extends Hyper { char s = 's'; }
class Test extends Super {
public static void printH(Hyper h) {
System.out.println(h.h);
}
public static void main(String[] args) {
printH(new Super());
}
}
is compiled and executed, producing the output:
h
Suppose that a new version of class Super is then compiled:
class Super { char s = 's'; }
This version of class Super is not a subclass of Hyper. If we then run the existing binaries of Hyper and Test with the new version of Super, then a VerifyError is thrown at link time. The verifier complains because the result of new Super() cannot be passed as an argument in place of a formal parameter of type Hyper, because Super is not a subclass of Hyper.
It is instructive to consider what might happen without the verification step: the program might run and print:
s
This demonstrates that without the verifier, the Java type system could be defeated by linking inconsistent binary files, even though each was produced by a correct Java compiler.
The lesson is that an implementation that lacks a verifier or fails to use it will not maintain type safety and is, therefore, not a valid implementation.
Example 13.4.4-2. Introducing a Superclass
Broadly speaking, there are various situations where a class transformation that is binary compatible for a client might not be source compatible for that client.
For example, the requirement that alternatives in a multi-catch clause (§14.20) are not subclasses or superclasses of each other is only a source restriction. The following code:
try {
failByThrowingAorB();
} catch (A|B e) {
...
}
is legal provided that A and B do not have a subclass/superclass relationship when the code is compiled. Thereafter, it is binary compatible with respect to this client for A and B to be changed to have such a relationship. The previously compiled code will continue to execute, but since the change is not source compatible with respect to this client, the code cannot be recompiled.
13.4.5. Class Type Parameters
Adding or deleting a type parameter of a class does not, in itself, have any implications for binary compatibility.
If such a type parameter is used in the type of a field or method, that may have the normal implications of changing the aforementioned type.
Renaming a type parameter of a class has no effect with respect to pre-existing binaries.
Changing the first bound of a type parameter of a class may change the erasure (§4.6) of any member that uses that type parameter in its own type, and this may affect binary compatibility. The change of such a bound is analogous to the change of the first bound of a type parameter of a method or constructor (§13.4.13).
Changing any other bound has no effect on binary compatibility.
13.4.6. Class Body and Member Declarations
No incompatibility with pre-existing binaries is caused by adding an instance (respectively static) member that has the same name and accessibility (for fields), or same name and accessibility and signature and return type (for methods), as an instance (respectively static) member of a superclass or subclass. No error occurs even if the set of classes being linked would encounter a compile-time error.
Deleting a class member or constructor that is not declared private may cause a linkage error if the member or constructor is used by a pre-existing binary.
Example 13.4.6-1. Changing A Class Body
class Hyper {
void hello() { System.out.println("hello from Hyper"); }
}
class Super extends Hyper {
void hello() { System.out.println("hello from Super"); }
}
class Test {
public static void main(String[] args) {
new Super().hello();
}
}
This program produces the output:
hello from Super
Suppose that a new version of class Super is produced:
class Super extends Hyper {}
Then, recompiling Super and executing this new binary with the original binaries for Test and Hyper produces the output:
hello from Hyper
as expected.
The super keyword can be used to access a method declared in a superclass, bypassing any methods declared in the current class. The expression super.Identifier is resolved, at compile time, to a method m in the superclass S. If the method m is an instance method, then the method which is invoked at run time is the method with the same signature as m that is a member of the direct superclass of the class containing the expression involving super.
Example 13.4.6-2. Changing A Superclass
class Hyper {
void hello() { System.out.println("hello from Hyper"); }
}
class Super extends Hyper { }
class Test extends Super {
public static void main(String[] args) {
new Test().hello();
}
void hello() {
super.hello();
}
}
This program produces the output:
hello from Hyper
Suppose that a new version of class Super is produced:
class Super extends Hyper {
void hello() { System.out.println("hello from Super"); }
}
Then, if Super and Hyper are recompiled but not Test, then running the new binaries with the existing binary of Test produces the output:
hello from Super
as you might expect.
13.4.7. Access to Members and Constructors
Changing the declared access of a member or constructor to permit less access may break compatibility with pre-existing binaries, causing a linkage error to be thrown when these binaries are resolved. Less access is permitted if the access modifier is changed from package access to private access; from protected access to package or private access; or from public access to protected, package, or private access. Changing a member or constructor to permit less access is therefore not recommended for widely distributed classes.
Perhaps surprisingly, the binary format is defined so that changing a member or constructor to be more accessible does not cause a linkage error when a subclass (already) defines a method to have less access.
Example 13.4.7-1. Changing Accessibility
If the package points defines the class Point:
package points;
public class Point {
public int x, y;
protected void print() {
System.out.println("(" + x + "," + y + ")");
}
}
used by the program:
class Test extends points.Point {
public static void main(String[] args) {
Test t = new Test();
t.print();
}
protected void print() {
System.out.println("Test");
}
}
then these classes compile and Test executes to produce the output:
Test
If the method print in class Point is changed to be public, and then only the Point class is recompiled, and then executed with the previously existing binary for Test, then no linkage error occurs. This happens even though it is improper, at compile time, for a public method to be overridden by a protected method (as shown by the fact that the class Test could not be recompiled using this new Point class unless print in Test were changed to be public.)
Allowing superclasses to change protected methods to be public without breaking binaries of pre-existing subclasses helps make binaries less fragile. The alternative, where such a change would cause a linkage error, would create additional binary incompatibilities.
13.4.8. Field Declarations
Widely distributed programs should not expose any fields to their clients. Apart from the binary compatibility issues discussed below, this is generally good software engineering practice. Adding a field to a class may break compatibility with pre-existing binaries that are not recompiled.
Assume a reference to a field f with qualifying class C. Assume further that f is in fact an instance (respectively static) field declared in a superclass of C, S, and that the type of f is X.
If a new field of type X with the same name as f is added to a subclass of S that is a superclass of C or C itself, then a linkage error may occur. Such a linkage error will occur only if, in addition to the above, either one of the following is true:
The new field is less accessible than the old one.
The new field is a static (respectively instance) field.
In particular, no linkage error will occur in the case where a class could no longer be recompiled because a field access previously referenced a field of a superclass with an incompatible type. The previously compiled class with such a reference will continue to reference the field declared in a superclass.
Example 13.4.8-1. Adding A Field Declaration
class Hyper { String h = "hyper"; }
class Super extends Hyper { String s = "super"; }
class Test {
public static void main(String[] args) {
System.out.println(new Super().h);
}
}
This program produces the output:
hyper
Suppose a new version of class Super is produced:
class Super extends Hyper {
String s = "super";
int h = 0;
}
Then, recompiling Hyper and Super, and executing the resulting new binaries with the old binary of Test produces the output:
hyper
The field h of Hyper is output by the original binary of Test. While this may seem surprising at first, it serves to reduce the number of incompatibilities that occur at run time. (In an ideal world, all source files that needed recompilation would be recompiled whenever any one of them changed, eliminating such surprises. But such a mass recompilation is often impractical or impossible, especially in the Internet. And, as was previously noted, such recompilation would sometimes require further changes to the source code.)
As another example, if the program:
class Hyper { String h = "Hyper"; }
class Super extends Hyper { }
class Test extends Super {
public static void main(String[] args) {
String s = new Test().h;
System.out.println(s);
}
}
is compiled and executed, it produces the output:
Hyper
Suppose that a new version of class Super is then compiled:
class Super extends Hyper { char h = 'h'; }
If the resulting binary is used with the existing binaries for Hyper and Test, then the output is still:
Hyper
even though compiling the source for these binaries:
class Hyper { String h = "Hyper"; }
class Super extends Hyper { char h = 'h'; }
class Test extends Super {
public static void main(String[] args) {
String s = new Test().h;
System.out.println(s);
}
}
would result in a compile-time error, because the h in the source code for main would now be construed as referring to the char field declared in Super, and a char value can't be assigned to a String.
Deleting a field from a class will break compatibility with any pre-existing binaries that reference this field, and a NoSuchFieldError will be thrown when such a reference from a pre-existing binary is linked. Only private fields may be safely deleted from a widely distributed class.
For purposes of binary compatibility, adding or deleting a field f whose type involves type variables (§4.4) or parameterized types (§4.5) is equivalent to the addition (respectively, deletion) of a field of the same name whose type is the erasure (§4.6) of the type of f.
13.4.9. final Fields and static Constant Variables
If a field that was not declared final is changed to be declared final, then it can break compatibility with pre-existing binaries that attempt to assign new values to the field.
Example 13.4.9-1. Changing A Variable To Be final
class Super { char s; }
class Test extends Super {
public static void main(String[] args) {
Super x = new Super();
x.s = 'a';
System.out.println(x.s);
}
}
This program produces the output:
a
Suppose that a new version of class Super is produced:
class Super { final char s = 'b'; }
If Super is recompiled but not Test, then running the new binary with the existing binary of Test results in a IllegalAccessError.
Deleting the keyword final or changing the value to which a field is initialized does not break compatibility with existing binaries.
If a field is a constant variable (§4.12.4), and moreover is static, then deleting the keyword final or changing its value will not break compatibility with pre-existing binaries by causing them not to run, but they will not see any new value for a usage of the field unless they are recompiled. This result is a side-effect of the decision to support conditional compilation (§14.22). (One might suppose that the new value is not seen if the usage occurs in a constant expression (§15.29) but is seen otherwise. This is not so; pre-existing binaries do not see the new value at all.)
The best way to avoid problems with "inconstant constants" in widely-distributed code is to use static constant variables only for values which truly are unlikely ever to change. Other than for true mathematical constants, we recommend that source code make very sparing use of static constant variables.
If the read-only nature of final is required, a better choice is to declare a private static variable and a suitable accessor method to get its value. Thus we recommend:
private static int N;
public static int getN() { return N; }
rather than:
public static final int N = ...;
There is no problem with:
public static int N = ...;
if N need not be read-only.
13.4.10. static Fields
If a field that is not declared private was not declared static and is changed to be declared static, or vice versa, then a linkage error, specifically an IncompatibleClassChangeError, will result if the field is used by a pre-existing binary which expected a field of the other kind. Such changes are not recommended in code that has been widely distributed.
13.4.11. transient Fields
Adding or deleting a transient modifier of a field does not break compatibility with pre-existing binaries.
13.4.12. Method and Constructor Declarations
Adding a method or constructor to a class will not break compatibility with any pre-existing binaries, even in the case where a class could no longer be recompiled because an invocation previously referenced a method or constructor of a superclass with an incompatible type. The previously compiled class with such a reference will continue to reference the method or constructor declared in a superclass.
Assume a reference to a method m with qualifying class C. Assume further that m is in fact an instance (respectively static) method declared in a superclass of C, S.
If a new method of type X with the same signature and return type as m is added to a subclass of S that is a superclass of C or C itself, then a linkage error may occur. Such a linkage error will occur only if, in addition to the above, either one of the following is true:
The new method is less accessible than the old one.
The new method is a static (respectively instance) method.
Deleting a method or constructor from a class may break compatibility with any pre-existing binary that referenced this method or constructor; a NoSuchMethodError may be thrown when such a reference from a pre-existing binary is linked. Such an error will occur only if no method with a matching signature and return type is declared in a superclass.
If the source code for a non-inner class contains no declared constructors, then a default constructor with no parameters is implicitly declared (§8.8.9). Adding one or more constructor declarations to the source code of such a class will prevent this default constructor from being implicitly declared, effectively deleting a constructor, unless one of the new constructors also has no parameters, thus replacing the default constructor. The default constructor with no parameters is given the same access modifier as the class of its declaration, so any replacement should have as much or more access if compatibility with pre-existing binaries is to be preserved.
13.4.13. Method and Constructor Type Parameters
Adding or deleting a type parameter of a method or constructor does not, in itself, have any implications for binary compatibility.
If such a type parameter is used in the type of the method or constructor, that may have the normal implications of changing the aforementioned type.
Renaming a type parameter of a method or constructor has no effect with respect to pre-existing binaries.
Changing the first bound of a type parameter of a method or constructor may change the erasure (§4.6) of any member that uses that type parameter in its own type, and this may affect binary compatibility. Specifically:
If the type parameter is used as the type of a field, the effect is as if the field were deleted and a field with the same name, whose type is the new erasure of the type variable, were added.
If the type parameter is used as the type of any formal parameter of a method, but not as the return type, the effect is as if that method were deleted, and replaced with a new method that is identical except for the types of the aforementioned formal parameters, which now have the new erasure of the type parameter as their type.
If the type parameter is used as a return type of a method, but not as the type of any formal parameter of the method, the effect is as if that method were deleted, and replaced with a new method that is identical except for the return type, which is now the new erasure of the type parameter.
If the type parameter is used as a return type of a method and as the type of one or more formal parameters of the method, the effect is as if that method were deleted, and replaced with a new method that is identical except for the return type, which is now the new erasure of the type parameter, and except for the types of the aforementioned formal parameters, which now have the new erasure of the type parameter as their types.
Changing any other bound has no effect on binary compatibility.
13.4.14. Method and Constructor Formal Parameters
Changing the name of a formal parameter of a method or constructor does not impact pre-existing binaries.
Changing the name of a method, or the type of a formal parameter to a method or constructor, or adding a parameter to or deleting a parameter from a method or constructor declaration creates a method or constructor with a new signature, and has the combined effect of deleting the method or constructor with the old signature and adding a method or constructor with the new signature (§13.4.12).
Changing the type of the last formal parameter of a method from T[] to a variable arity parameter of type T, that is, to T... (§8.4.1), and vice versa, does not impact pre-existing binaries.
For purposes of binary compatibility, adding or deleting a method or constructor m whose signature involves type variables (§4.4) or parameterized types (§4.5) is equivalent to the addition (respectively, deletion) of an otherwise equivalent method whose signature is the erasure (§4.6) of the signature of m.
13.4.15. Method Result Type
Changing the result type of a method, or replacing a result type with void, or replacing void with a result type, has the combined effect of deleting the old method and adding a new method with the new result type or newly void result (see §13.4.12).
For purposes of binary compatibility, adding or deleting a method or constructor m whose return type involves type variables (§4.4) or parameterized types (§4.5) is equivalent to the addition (respectively, deletion) of the an otherwise equivalent method whose return type is the erasure (§4.6) of the return type of m.
13.4.16. abstract Methods
Changing a method that is declared abstract to no longer be declared abstract does not break compatibility with pre-existing binaries.
Changing a method that is not declared abstract to be declared abstract will break compatibility with pre-existing binaries that previously invoked the method, causing an AbstractMethodError.
Example 13.4.16-1. Changing A Method To Be abstract
class Super { void out() { System.out.println("Out"); } }
class Test extends Super {
public static void main(String[] args) {
Test t = new Test();
System.out.println("Way ");
t.out();
}
}
This program produces the output:
Way
Out
Suppose that a new version of class Super is produced:
abstract class Super {
abstract void out();
}
If Super is recompiled but not Test, then running the new binary with the existing binary of Test results in an AbstractMethodError, because class Test has no implementation of the method out, and is therefore is (or should be) abstract.
13.4.17. final Methods
Changing a method that is declared final to no longer be declared final does not break compatibility with pre-existing binaries.
Changing an instance method that is not declared final to be declared final may break compatibility with existing binaries that depend on the ability to override the method.
Example 13.4.17-1. Changing A Method To Be final
class Super { void out() { System.out.println("out"); } }
class Test extends Super {
public static void main(String[] args) {
Test t = new Test();
t.out();
}
void out() { super.out(); }
}
This program produces the output:
out
Suppose that a new version of class Super is produced:
class Super { final void out() { System.out.println("!"); } }
If Super is recompiled but not Test, then running the new binary with the existing binary of Test results in an IncompatibleClassChangeError because the class Test improperly tries to override the instance method out.
Changing a class (static) method that is not declared final to be declared final does not break compatibility with existing binaries, because the method could not have been overridden.
13.4.18. native Methods
Adding or deleting a native modifier of a method does not break compatibility with pre-existing binaries.
The impact of changes to types on pre-existing native methods that are not recompiled is beyond the scope of this specification and should be provided with the description of an implementation. Implementations are encouraged, but not required, to implement native methods in a way that limits such impact.
13.4.19. static Methods
If a method that is not declared private is also declared static (that is, a class method) and is changed to not be declared static (that is, to an instance method), or vice versa, then compatibility with pre-existing binaries may be broken, resulting in a linkage time error, namely an IncompatibleClassChangeError, if these methods are used by the pre-existing binaries. Such changes are not recommended in code that has been widely distributed.
13.4.20. synchronized Methods
Adding or deleting a synchronized modifier of a method does not break compatibility with pre-existing binaries.
13.4.21. Method and Constructor Throws
Changes to the throws clause of methods or constructors do not break compatibility with pre-existing binaries; these clauses are checked only at compile time.
13.4.22. Method and Constructor Body
Changes to the body of a method or constructor do not break compatibility with pre-existing binaries.
The keyword final on a method does not mean that the method can be safely inlined; it means only that the method cannot be overridden. It is still possible that a new version of that method will be provided at link-time. Furthermore, the structure of the original program must be preserved for purposes of reflection.
Therefore, we note that a Java compiler cannot expand a method inline at compile time. In general we suggest that implementations use late-bound (run-time) code generation and optimization.
13.4.23. Method and Constructor Overloading
Adding new methods or constructors that overload existing methods or constructors does not break compatibility with pre-existing binaries. The signature to be used for each invocation was determined when these existing binaries were compiled; therefore newly added methods or constructors will not be used, even if their signatures are both applicable and more specific than the signature originally chosen.
While adding a new overloaded method or constructor may cause a compile-time error the next time a class or interface is compiled because there is no method or constructor that is most specific (§15.12.2.5), no such error occurs when a program is executed, because no overload resolution is done at execution time.
Example 13.4.23-1. Adding An Overloaded Method
class Super {
static void out(float f) {
System.out.println("float");
}
}
class Test {
public static void main(String[] args) {
Super.out(2);
}
}
This program produces the output:
float
Suppose that a new version of class Super is produced:
class Super {
static void out(float f) { System.out.println("float"); }
static void out(int i) { System.out.println("int"); }
}
If Super is recompiled but not Test, then running the new binary with the existing binary of Test still produces the output:
float
However, if Test is then recompiled, using this new Super, the output is then:
int
as might have been naively expected in the previous case.
13.4.24. Method Overriding
If an instance method is added to a subclass and it overrides a method in a superclass, then the subclass method will be found by method invocations in pre-existing binaries, and these binaries are not impacted.
If a class method is added to a class, then this method will not be found unless the qualifying class of the method invocation is the subclass.
13.4.25. Static Initializers
Adding, deleting, or changing a static initializer (§8.7) of a class does not impact pre-existing binaries.
13.4.26. Evolution of Enum Classes
Adding or reordering enum constants in an enum class will not break compatibility with pre-existing binaries.
As with sealed classes (§13.4.2.1), although adding an enum constant to an enum class is considered a binary compatible change, it may cause the execution of an exhaustive switch (§14.11.1) to fail if the switch encounters the new enum constant that was not known at compile time (§14.11.3, §15.28.2).
Deleting an enum constant from an enum class will delete the public field that corresponds to the enum constant (§8.9.3). The consequences are specified in §13.4.8. Such a change is not recommended for widely distributed enum classes.
In all other respects, the binary compatibility rules for enum classes are identical to those for normal classes.
13.4.27. Evolution of Record Classes
Adding, deleting, changing, or reordering record components in a record class may break compatibility with pre-existing binaries that are not recompiled; such a change is not recommended for widely distributed record classes.
More precisely, adding, deleting, changing, or reordering record components may change the corresponding implicit declarations of component fields and accessor methods, as well as changing the signature and implementation of the canonical constructor and other supporting methods, with consequences specified in §13.4.8 and §13.4.12.
In all other respects, the binary compatibility rules for record classes are identical to those for normal classes.
13.5. Evolution of Interfaces
This section describes the impact of changes to the declaration of an interface and its members on pre-existing binaries.
13.5.1. public Interfaces
Changing an interface that is not declared public to be declared public does not break compatibility with pre-existing binaries.
If an interface that is declared public is changed to not be declared public, then an IllegalAccessError is thrown if a pre-existing binary is linked that needs but no longer has access to the interface type, so such a change is not recommended for widely distributed interfaces.
13.5.2. sealed and non-sealed Interfaces
If an interface that was freely extensible (§9.1.1.4) is changed to be declared sealed, then an IncompatibleClassChangeError is thrown if a binary of a pre-existing subclass or subinterface of this interface is loaded and is not a permitted direct subclass or subinterface of this interface (§9.1.4); such a change is not recommended for widely distributed classes.
Adding a class or interface to the set of permitted direct subclasses or subinterfaces, respectively, of a sealed interface will not break compatibility with pre-existing binaries.
As with sealed classes (§13.4.2.1), whilst adding a permitted direct subclass or subinterface of a sealed interface is considered a binary compatible change, it may cause the execution of an exhaustive switch (§14.11.1) to fail with an error (a MatchException may be thrown) if the switch encounters an instance of the new permitted direct subclass or subinterface that was not known at compile time (§14.11.3, §15.28.2).
If a class or interface is removed from the set of permitted direct subclasses or subinterfaces of a sealed interface, then an IncompatibleClassChangeError is thrown if the pre-existing binary of the removed class or interface is loaded.
Changing an interface that was declared sealed to be declared non-sealed does not break compatibility with pre-existing binaries.
A non-sealed interface I must have a sealed direct superinterface. Deleting the non-sealed modifier would prevent I from being recompiled, as every interface with a sealed direct superinterface must be sealed or non-sealed.
Deleting the sealed modifier from an interface that does not have a sealed direct superinterface does not break compatibility with pre-existing binaries.
If a sealed interface I did have a sealed direct superinterface, then deleting the sealed modifier would prevent I from being recompiled, as every interface with a sealed direct superinterface must be sealed or non-sealed.
13.5.3. Superinterfaces
Changes to the interface hierarchy cause errors in the same way that changes to the class hierarchy do, as described in §13.4.4. In particular, changes that result in any previous superinterface of a class no longer being a superinterface can break compatibility with pre-existing binaries, resulting in a VerifyError.
13.5.4. Interface Members
Adding an abstract, private, or static method to an interface does not break compatibility with pre-existing binaries.
Adding a field to a superinterface of C may hide a field inherited from a superclass of C. If the original reference was to an instance field, an IncompatibleClassChangeError will result. If the original reference was an assignment, an IllegalAccessError will result.
Deleting a member from an interface may cause linkage errors in pre-existing binaries.
Example 13.5.4-1. Deleting An Interface Member
interface I { void hello(); }
class Test implements I {
public static void main(String[] args) {
I anI = new Test();
anI.hello();
}
public void hello() { System.out.println("hello"); }
}
This program produces the output:
hello
Suppose that a new version of interface I is compiled:
interface I {}
If I is recompiled but not Test, then running the new binary with the existing binary for Test will result in a NoSuchMethodError.
13.5.5. Interface Type Parameters
The effects of changes to the type parameters of an interface are the same as those of analogous changes to the type parameters of a class.
13.5.6. Field Declarations
The considerations for changing field declarations in interfaces are the same as those for static final fields in classes, as described in §13.4.8 and §13.4.9.
13.5.7. Interface Method Declarations
The considerations for changing method declarations in interfaces include those for changing methods in classes, as described in §13.4.7, §13.4.14, §13.4.15, §13.4.19, §13.4.21, §13.4.22, and §13.4.23.
Adding a default method, or changing a method from abstract to default, does not break compatibility with pre-existing binaries, but may cause an IncompatibleClassChangeError if a pre-existing binary attempts to invoke the method. This error occurs if the qualifying interface of the method invocation, K, is a subinterface of two interfaces, I and J, where both I and J declare a default method with the same signature and result, and neither I nor J is a subinterface of the other.
In other words, adding a default method is a binary-compatible change because it does not introduce errors at link time, even if it introduces errors at compile time or invocation time. In practice, the risk of accidental clashes occurring by introducing a default method are similar to those associated with adding a new method to a non-final class. In the event of a clash, adding a method to a class is unlikely to trigger a LinkageError, but an accidental override of the method in a child can lead to unpredictable method behavior. Both changes can cause errors at compile time.
Example 13.5.7-1. Adding A Default Method
interface Painter {
default void draw() {
System.out.println("Here's a picture...");