Contract Usage and Evolution in Android Mobile Applications
Abstract.
Formal contracts and assertions are effective methods to enhance software quality by enforcing preconditions, postconditions, and invariants. Previous research has demonstrated the value of contracts in traditional software development contexts. However, the adoption and impact of contracts in the context of mobile application development, particularly of Android applications, remain unexplored.
To address this, we present the first large-scale empirical study on the presence and use of contracts in Android applications, written in Java or Kotlin. We consider different types of contract elements divided into five categories: conditional runtime exceptions, APIs, annotations, assertions, and other. We analyzed 2,390 Android applications from the F-Droid repository and processed more than 51,749 KLOC to determine 1) how and to what extent contracts are used, 2) how contract usage evolves, and 3) whether contracts are used safely in the context of program evolution and inheritance. Our findings include: 1) although most applications do not specify contracts, annotation-based approaches are the most popular among practitioners; 2) applications that use contracts continue to use them in later versions, but the number of methods increases at a higher rate than the number of contracts; and 3) there are many potentially unsafe specification changes when applications evolve and in subty** relationships, which indicates a lack of specification stability. Our findings show that it would be desirable to have libraries that standardize contract specifications in Java and Kotlin, and tools that aid practitioners in writing stronger contracts and in detecting contract violations in the context of program evolution and inheritance.
1. Introduction
Building reliable mobile applications is a growing concern as many companies are adopting iOS and Android as target platforms for their apps in critical domains such as mobility, health, finance, and government. There are now more mobile phones than people in the world: in 2022, there were more than 8.58 billion mobile subscriptions in use worldwide,111https://www.weforum.org/agenda/2023/04/charted-there-are-more-phones-than-people-in-the-world/ (accessed 17 November 2023) with more than 2 million apps available on the App Store and Google Play (Tao and Edmunds, 2018). Additionally, data from 2022 shows that Android represents approximately 43% of the overall operative system market share, followed by Windows (29%), and then iOS (18%) (Stats, 2023). Therefore, faults in mobile apps, and particularly in Android apps, can impact a very large number of users. In addition, with an increasing number of apps in critical areas such as health and finance, faults can have a huge negative impact. It is thus important to use software reliability techniques when develo** mobile applications.
One of these techniques is Design by Contract (DbC) (Meyer, 1992), under which software systems are seen as components that interact amongst themselves based on precisely defined specifications of client-supplier obligations (contracts). Contracts are specifications of an agreement between the client and the supplier of a component, where the supplier expects that certain conditions are met by the client before using the component (preconditions), maintains certain properties from entry to the component to exit (invariants), and guarantees that certain properties are met upon exit (postconditions). These contracts are written as assertions directly into the code. Currently, there are assertion capabilities in most programming languages, but assertions are not universally used. Since the 1980s, many have advocated DbC (Meyer, 1988) as an efficient technique to aid the identification of failures (Aniche, 2022), improve code understanding (Fairbanks, 2019), and improve testing efforts (Tantivongsathaporn and Stearns, 2006) which directly or indirectly contribute to more reliable software. This has led to a number of empirical studies on the use of contracts in a variety of contexts (Chalin, 2006; Schiller et al., 2014; Estler et al., 2014; Casalnuovo et al., 2015; Kudrjavets et al., 2006; Kochhar and Lo, 2017; Counsell et al., 2017; Dietrich et al., 2017). However, there are no previous studies on the presence and usage of contracts in Android applications nor any study that includes the Kotlin language.
In this paper, we present the first large-scale empirical study of contract usage in Android mobile applications written in Java or Kotlin. Our goal is to understand what is the adoption rate of contracts among Android developers, their evolution, and which language features are used to denote contracts. Information on how practitioners use contracts can aid the creation and improvement of tools and libraries by researchers and tool builders (Schiller et al., 2014). Additionally, empirical evidence about the benefits of contracts can encourage their adoption by practitioners and the establishment of DbC as a software design standard (Tantivongsathaporn and Stearns, 2006).
We aim to determine: 1) how and to what extent contracts are used, 2) how contract usage evolves in an application, and 3) whether contracts are used safely in the context of program evolution and inheritance.
In summary, the contributions of this paper are:
-
•
The first large-scale empirical study about contract usage and evolution in Android applications, resulting in a list of findings and recommendations for practitioners, researchers, and tool builders. Also, no previous studies consider Kotlin programs.
-
•
A list of language features, tools, and libraries to represent contracts in Android applications.
-
•
A dataset of 1,767 Java and 623 Kotlin Android applications, together with a pipeline of scripts that can be used to build large-scale datasets of Java and Kotlin open-source projects, guided by inclusion criteria and size optimization.
-
•
An updated and extended version of Dietrich et al.’s tool (Dietrich et al., 2017), which can now analyze Kotlin code and can be used to investigate additional Android-specific contracts.
It should be noted that, even though we update and extend Dietrich et al. (2017)’s tool, our work is not a replication of their study. Our study differs from their work by focusing on Android applications and not on Java applications only. Due to the focus on Android, our study considers Kotlin in addition to Java, as since 2019, the Kotlin programming language is the preferred language for Android app developers222https://techcrunch.com/2019/05/07/kotlin-is-now-googles-preferred-language-for-android-app-development (accessed 17 November 2023). Moreover, Kotlin is now used by over 60% of Android professional developers333https://developer.android.com/kotlin (accessed 17 November 2023).
Data & Artifact Availability
To support our study, an artifact was developed to automatically collect contracts from Android applications and to produce the necessary empirical data. The artifact is written in Python and Java, and includes an extension of the tool proposed by Dietrich et al. (Dietrich et al., 2017). All the code and datasets are publicly available: https://figshare.com/s/d6eb7e5522bb535dc81a
2. Contracts in Android Applications
category | examples |
IllegalArgumentException}\\ (74 constructs) & \mintinlinejavaEmptyStackException |
|
SecurityException} \\ ~& \mintinlinejavaUnsupportedOperationException |
|
AccessControlException} \\ ~& \mintinlinejavaIndexOutOfBoundsException |
|
NullPointerException}\\ %See \autoreftab:cre-exceptions |
|
org.apache.commons.lang.Validate.*} \\ (31 constructs) & \mintinlinejavaorg.apache.commons.lang3.Validate.* |
|
com.google.common.base.Preconditions.*} \\ & \mintinlinejavaorg.springframework.util.Assert.* |
|
assert} (Java) \\ (6 constructs) & \mintinlinejavaassert (Kotlin) |
|
check(), checkNotNull()} (Kotlin) \\ & \mintinlinejavarequire(), requireNotNull() (Kotlin) |
|
org.jetbrains.annotations.*} \\ (136 constructs) & \mintinlinejavaorg.intellij.lang.annotations.* |
|
edu.umd.cs.findbugs.annotations.*} \\ & \mintinlinejavaandroid.annotation.* |
|
androidx.annotation.*} \\ & \mintinlinejavajavax.annotation.* (JSR305) |
|
Other |
Dataset metrics.
From the initial list of 4,070 projects in the F-Droid index retrieved on May 21, 2023, we got 3,215 hosted in GitHub, 3,141 non-duplicated URLs, and 2,390 projects after filtering by the inclusion criteria. Out of these, 1,767 are Java applications and 623 are Kotlin applications. Since we tried to retrieve two versions for each application, we analyzed 4,192 program-version pairs. This means that for 294 applications, it was only possible to retrieve a single version. While these applications are still evaluated in the context of the usage and LSP studies, they are not considered for the evolution study.
LABEL:tab:data-metrics presents additional metrics about the final dataset size used in the empirical evaluation. Additionally, the table shows that the dataset is imbalanced, with more Java applications. The dataset includes 204,468 Java and 123,222 Kotlin compilation units and, therefore, Java represents 62.4% of the overall number of compilation units. This imbalance requires caution when trying to read this work’s results from the perspective of comparing Java against Kotlin’s use of contracts. Furthermore, the dataset includes 551,456 classes, 2,682,160 methods, and 300,639 constructors. Note that we did not consider private methods, because those methods are not used directly by a client, and a contract is a bond between a supplier and a client. In total, we analyzed 2,532,399 public, protected, and internal methods, and constructors. It is also worth noting the difference in the number of compilation units and classes between the two languages. Although the dataset contains 1.67 times more Java compilation units than Kotlin ones, it only includes 1.19 times more Java classes. Since a compilation unit usually represents a file, this means that there are more classes per file in Kotlin (2.04) than in Java (1.47). This is expected due to the more restricted Java rules that, for example, only allow a single top-level public class per file.
From the standpoint of the dataset’s diversity, it includes apps from various domains, such as gaming, communication, multimedia, security, health, and productivity.
4.3. Data Collection and Analysis
Here, we describe the analysis tool used and the three studies that we conducted to answer our research questions: the usage study, the evolution study, and the Liskov Substitution Principle study.
4.3.1. Analysis Tool
Our analysis tool is an extension of the tool created by Dietrich et al. (Dietrich et al., 2017), which was used in their study on the usage of contracts in Java applications. We extended the tool to support Kotlin source code and more constructs focused on Android applications. Additionally, the framework’s code also suffered considerable refactoring and organization to simplify and ease its comprehension and maintainability. The main effort was to add support for Kotlin source code. The original tool was using the JavaParser888https://javaparser.org (accessed 17 November 2023) library to perform AST analysis of Java code. Since this library is not able to parse Kotlin source code, we integrated JetBrains’s Kotlin compiler999https://github.com/JetBrains/kotlin (accessed 4 June 2023) to perform this task. This required us to implement new versions of the tool’s extractors and visitors classes using the methods provided by the new library to be able to identify contract patterns in Kotlin. We also updated the JavaParser library to support newer Java versions.
The tool is divided into three main parts: 1) usage, which extracts the list of contracts present in each program and produces statistics about their use; 2) inheritance, which identifies contracts in overridden methods and validates whether they violate the Liskov Substitution Principle; and 3) evolution, which analyses how identified contracts evolve in later versions of the application. The following sections describe how each component contributes to answering the proposed research questions.
4.3.2. Usage Study
The usage study is divided in two main steps: 1) identifying contract occurrences and 2) producing statistics about those results. Our tool uses the JavaParser and JetBrains’s Kotlin compiler libraries to perform AST analysis of all dataset’s source code file that is either Java or Kotlin. This analysis is done against a set of extractors to identify occurrences of our defined constructs. Each category requires different approaches for their identification:
-
•
CREs. During the AST analysis, we look for the pattern:
if (<condition>) { throw new <exception> (<args>) } When this pattern is found, we check whether the exception belongs to the list of CREs considered (see Section 2). In line with Java’s good practices, we assume that CREs are used with preconditions.
-
•
APIs. Firstly, we check whether the file contains an import declaration to any API package listed in Table 4 . If any is found, all call expressions in that file are analyzed to determine if they are invoking any of the methods provided by the API. As stated in LABEL:paragraph:APIs, we assume the analyzed APIs to be associated with preconditions.
-
•
Assertions. Identifying Java asserts is straightforward since the JavaParser provides a visitor method for this particular statement. The complexity lies in identifying Kotlin asserts, which is not a reserved keyword. To handle this challenge, when analyzing a file, we first search for any method declaration and any import statement that has a name equal to one of the following expressions: assert, require, requireNotNull, check, checkNotNull. Next, we identify whether the class invokes any method with one of those names. Suppose a class has a method declaration/import statement and an invocation with one of these expression’s names. In that case, we consider it an ambiguous situation, and therefore, we do not consider it an assert instance. If the class invokes one of those methods but does not declare/import any method with that same name, we consider it an assert. This is not a fool-proof approach, but it minimizes the under-reporting to a residual level. We do not classify assertions either as preconditions or postconditions.
-
•
Annotations. We check if the source code file contains an import statement to one of the packages listed in section 2. If that is the case, we check every annotation in that file to see if it matches any of those provided by the imported package. We also identify the artifact to which the annotation is associated as follows: 1) annotations associated with a method’s parameters are preconditions; 2) annotations associated with a method are postconditions; and 3) annotations associated with a field are class invariants.
-
•
Others. This category only includes the investigation of the experimental Kotlin Contracts. To identify occurrences of this construct, we look for the pattern - contract {returns (¡condition¿) implies (¡condition¿)}.
Our tool creates a JSON file for each program-version pair that stores the identified contracts, including 1) the file path, 2) the associated condition, 3) the method or property name, 4) the type of artifact (method or property), 5) the line number, and 6) the contract type. In the second step of the usage study, all the JSON files are analyzed to produce statistics about the identified contracts, including the frequency of each category (API, annotation, assertion, etc.), class (preconditions, postconditions, and class invariants), construct (java assert, Guava API, androidx annotations, etc.), to compute the Gini coefficient for each category, and to list the programs with more contracts for each category.
4.3.3. Evolution Study
In the evolution study, we are interested in knowing what happens to contracts while the application evolves. In other words, after identifying a contract in the first version of the application, we check whether, in the later version, the contract still exists, was modified or removed. At the same time, we are also reporting cases when a contract is added to an artifact (method or parameter) in the later version of the app (but was not present in the first version). This information provides insights into how contracts evolve in an application and whether this evolution poses risks to the client.
As already mentioned, a contract establishes rights and obligations for each part — the client assumes that the supplier will keep its obligations and vice-versa. Therefore, when a contract is altered, both parts should be informed and updated accordingly. This is particularly crucial when a precondition is strengthened or when a postcondition is weakened. In the first case, if the precondition is strengthened and the client does not know it, it can fail to cover its new obligations, and, therefore, the supplier is not bound to keep its part of the contract. In the latter case, if the postcondition is weakened, the client may still be making assumptions that the supplier does not ensure anymore. An example is shown in LABEL:lst:3-evolution-evolution-0, where the annotation @NonNull was added to the toolbar parameter in the last version. This is the case of a precondition strengthening: in the first version, the method accepted a null toolbar, but now it requires it to be not null. Therefore, if the client is not updated, it will fail to cover its new obligation.
To conduct this study, we follow the same approach as Dietrich et al. (Dietrich et al., 2017), firstly creating diff records from the contracts present at the two versions of a program’s method and then classifying them according to the evolution patterns listed in Table 3.
Classification | Description | Risk |
---|---|---|
PreconditionsStrengthened |
A precondition was added to a method or a clause to an existing precondition with the ’&’ or ’&&’ operators. |
Potential risk |
PreconditionsWeakened |
A precondition was removed from a method, or a clause was added to an existing precondition with the ’—’ or ’——’ operators. |
No risk. |
PostconditionsStrengthened |
A postcondition was added to a method or a clause to an existing postcondition with the ’&’ or ’&&’ operators. |
No risk. |
PostconditionsWeakened |
A postcondition was removed from a method, or a clause was added to an existing postcondition with the ’—’ or ’——’ operators. |
Potential risk. |
4.3.4. Liskov Substitution Principle Study
When a method is overridden in a subclass, that class can specify new contracts added to the ones inherited from the superclass method. In this case, proper handling of contracts should follow the Liskov Substitution Principle (LSP), which states that the subclass method must accept all input that is valid to the superclass method and meet all guarantees made by the superclass method. In other words, a subclass method can only weaken preconditions and strengthen postconditions.
To detect those occurrences, the analysis tool first lists all methods in each program-version pair associated with their respective class. Additionally, it also identifies the class’ parents. Then, similarly to the evolution study, diff records are created between the subclass and the superclass methods. These records are classified according to the evolution patterns described in Table 3.
5. Results
In this section, we present the results of our empirical study, as well as the main findings.
5.1. RQ1: Contract Usage
Table 4 shows the number of contracts found per category, considering all versions (columns 2 and 3) and considering only the latest version of each application (columns 4 and 5). The table also identifies the number of applications containing at least one contract for that category (columns 6 and 7). The most obvious conclusion is that, in both languages, annotation-based contracts are the most popular category. More specifically, considering both languages in the last version, annotations represent 87.1% of the contracts found, followed by CRE with 9.7%, and then assertions with 2.5%. The results show similar tendencies between Java and Kotlin, and the only difference is that while Java’s second most popular category is CREs, in Kotlin, it is assertions. This relatively high percentage of the assertion category in Kotlin is explained by our inclusion of the four language’s standard library methods listed in Section 2, where require() alone counts 901 total occurrences distributed by 112 last versions.
This distribution in categories’ popularity significantly differs from the findings of Dietrich et al. (Dietrich et al., 2017), who reported that the most common category was CREs and found surprisingly low use of annotations. This may be explained by the difference between the datasets’ nature. While our dataset is formed mostly by user-focused Android applications, the author’s dataset was mainly Java libraries. In Table 14, we can also see that most annotations found belong to the androidx.annotation.* package that the authors did not consider since it is Android-specific. Nevertheless, the high number of annotation-based contracts found is in line with literature that supports its increasing popularity (Yu et al., 2021; Grazia and Pradel, 2022).
From Table 4, we also verify that the usage of APIs is very low in both languages, and it is even more residual in Kotlin applications, where only nine instances were found in the latest versions. The known industry skepticism around adding third-party dependencies to projects, which may lead to maintainability and support issues in the future, may explain this finding (Backes et al., 2016; Wang et al., 2020).
contracts (all ver.) | contracts (2nd ver.) | applications | ||||
---|---|---|---|---|---|---|
Category | Java | Kotlin | Java | Kotlin | Java | Kotlin |
API | 1,813 | 10 | 1,121 | 9 | 24 | 4 |
annotation | 158,400 | 24,125 | 139,507 | 15,068 | 1,097 | 541 |
assertion | 3,525 | 3,746 | 2,186 | 2,239 | 326 | 232 |
CRE | 26,061 | 3,292 | 15,150 | 2,139 | 789 | 287 |
other | - | 1 | - | 1 | - | 1 |
In Table 14, we have a more detailed perspective by having the frequency of each construct. Firstly, we again highlight that the high number of annotations found is leveraged mostly by the androidx.annotation.* package. In APIs, the Guava library constitutes most of the usage. We were not expecting to see any usage of Spring Framework Asserts since this library was designed to be used in the Spring framework, but we still found one occurrence. At the same time, we found no occurrences of the now deprecated FindBugs annotations. Additionally, we identified a single occurrence of Kotlin Contracts, which may depict the practitioner’s distrust of using a feature still in an experimental phase.
We now consider Table 5, which presents each category’s computed Gini coefficient. The Gini coefficient measures the inequality among the values of a frequency distribution.
Category | Java | Kotlin |
---|---|---|
assertion | 0.70 | 0.71 |
API | 0.80 | 0.37 |
annotation | 0.88 | 0.76 |
CRE | 0.77 | 0.67 |
others | - | 1.00 |
In other words, a Gini coefficient of 0 indicates perfect equality, where all applications have the same number of contracts. In contrast, a Gini coefficient of 1 means that a single program has all the contracts. We observe that all coefficients in the table are higher than 0.50, except for Kotlin’s API usage. The fact that almost all coefficients are very high (close to 1) means that although some applications use contracts intensively, the majority does not use them significantly. This aligns with the results found by Dietrich et al. (Dietrich et al., 2017). This conclusion can also be seen in Table 7, where the five projects that use more contracts per category are listed. We find that a small group of projects own a large percentage of the overall use in each category. Additionally, it is clearly visible from the assertion and CRE categories that the numbers quickly decrease through the first to the fifth application showing the unbalanced usage between applications.
contracts (all ver.) | contracts (2nd ver.) | ||||
Construct | Category | Java | Kotlin | Java | Kotlin |
cond. runtime exc. | CRE | 25,565 | 3,232 | 14,887 | 2,071 |
unsupp. op. exc. | CRE | 511 | 142 | 308 | 116 |
java assert | assertion | 3,525 | - | 2,217 | - |
kotlin assert | assertion | - | 3,868 | - | 2,370 |
guava precond. | API | 1,798 | 10 | 1,121 | 9 |
commons validate | API | 148 | 0 | 3 | 0 |
spring assert | API | 1 | 0 | 1 | 0 |
JSR303, JSR349 | annotation | 0 | 0 | 0 | 0 |
JSR305 | annotation | 4,195 | 20 | 2,133 | 13 |
findbugs | annotation | 0 | 0 | 0 | 0 |
jetbrains | annotation | 2,310 | 138 | 1,596 | 98 |
android | annotation | 12,003 | 5,704 | 7,013 | 3,414 |
androidx | annotation | 139,933 | 20,593 | 86,212 | 13,811 |
kotlin contracts | others | - | 1 | - | 1 |
Category | Applications |
---|---|
assertion |
K1rakishou-Kuroba-Experimental (378), a-pavlov-jed2k (314), abhijitvalluri-fitnotifications (143), thundernest-k-9 (114), mozilla-mobile-firefox-android-klar (95) |
CRE |
redfish64-TinyTravelTracker (1,036), nikita36078-J2ME-Loader (690), abhijitvalluri-fitnotifications (561), lz233-unvcode-android (561), cmeng-git-atalk-android (447) |
API |
wbaumann-SmartReceiptsLibrary (534), alexcustos-linkasanote (318), BrandroidTools-OpenExplorer (69), oshepherd-Impeller (33), MovingBlocks-DestinationSol (30), inputmice-lttrs-android (24) |
annotation |
MuntashirAkon-AppManager (5,957), Forkgram-TelegramAndroid (5,552), Telegram-FOSS-Team-Telegram-FOSS (5,549), MarcusWolschon-osmeditor4android (4,393), NekoX-Dev-NekoX (4,032) |
other |
zhanghai-MaterialFiles (1) |
Lastly, Table 8 presents the frequency of each contract type. Once again, we have distinct results for Java and Kotlin. In Java, we found 63.73% of the classified instances in the last versions to be preconditions, 23.19% postconditions, and only 13.08% class invariants. These results align with other studies on contracts (Chalin, 2006; Schiller et al., 2014; Dietrich et al., 2017) that show a clear preference towards preconditions. However, Kotlin’s results are different from this expected preference hierarchy. From the classified instances of the last versions, we found 38.82% to be postconditions, 31.73% class invariants, and 29.44% preconditions. This suggests that Kotlin developers tend to favor postconditions over any other type, while preconditions come at the last position. Also, according to the classification described in Section 4.3.2, in our study, only annotations may be classified as postconditions or class-invariants. This means that in Kotlin, there is a higher number of annotations associated with the method’s return values and class properties than with the method’s parameters.
contracts (all ver.) | contracts (2nd ver.) | applications | ||||
---|---|---|---|---|---|---|
Type | Java | Kotlin | Java | Kotlin | Java | Kotlin |
pre-condition | 120,671 | 9,203 | 72,160 | 5,744 | 989 | 349 |
post-condition | 41,764 | 11,490 | 26,253 | 7,575 | 829 | 435 |
invariants | 23,836 | 9,122 | 14,811 | 6,190 | 643 | 348 |
not classified | 3,584 | 3,893 | 2,267 | 2,394 | 279 | 202 |
Although we can not provide a reason for this finding with certainty, we argue that the difference in practitioners’ preference for each type reported in Table 8 could stem from different behavior patterns which are demonstrated in Table 9 and Table 10. These tables list the ten most occurring constructs for each type in the last versions of Java and Kotlin applications. To create these tables, we followed the classification described in Section 4.3.2; hence, for example, although there are 2,217 instances of JavaAssert in Java, these were not included in the list since the analysis tool does not classify asserts by type.
By comparing the two tables, we draw distinct behavior patterns between the two languages. In the Kotlin constructs reported by Table 10, none of the top ten most popular constructs relates to null-checking. But in Java’s instances, reported in Table 9, 82.03% of preconditions and 71.12% of postconditions are associated with null-checking. In this number, we are not considering potential CREIllegalArgumentException and CREIllegalStateException that could be associated with null-checking since this would require analyzing the condition present at the if-statement. This confirms a lack of expressiveness in the contracts specified by Java practitioners, with most being associated with null-checking, which aligns with previous studies (Schiller et al., 2014; Estler et al., 2014).
This contrast in null-checking contracts between Java and Kotlin is easily explained by the languages’ different takes on nullability. In Kotlin, contrary to Java, regular types are non-nullable by default; therefore, in most cases, practitioners do not have the need for constructs like AndroidXNonNull or JSR305NonNull. On the other hand, it is interesting to observe that relaxing this constraint to allow nullable types is not a common practice since we found no meaningful use of constraints like AndroidXNullable and similar in Kotlin.
Pre-conditions | Post-conditions |
---|---|
AndroidXNonNull (36,031) | AndroidXNonNull (10,359) |
AndroidXNullable (14,983) | AndroidXNullable (5,954) |
CREIllegalArgumentException (7,663) | AndroidSuppressLint (3,125) |
CREIllegalStateException (3,232) | AndroidTargetApi (1,243) |
CRENullPointerException (2,230) | AndroidXRequiresApi (732) |
GuavaPreconditionNotNull (1,021) | AndroidXWorkerThread (551) |
JSR305NonNull (860) | AndroidXKeep (398) |
AndroidXStringRes (660) | AndroidXCallSuper (380) |
CREIndexOutOfBoundsException (656) | AndroidXUiThread (326) |
JetBrainsNotNull (612) | JSR305NonNull(322) |
Pre-conditions | Post-conditions |
---|---|
AndroidXStringRes (1,142) | AndroidSuppressLint (2,289) |
CREIllegalStateException (772) | AndroidXVisibleForTesting (1,663) |
CREIllegalArgumentException (748) | AndroidXRequiresApi (720) |
AndroidXColorInt (523) | AndroidXWorkerThread (638) |
AndroidXDrawableRes (425) | AndroidXMainThread (441) |
AndroidXAttrRes (255) | AndroidXCallSuper (319) |
AndroidXColorRes (195) | AndroidXColorInt (237) |
AndroidXIdRes (184) | AndroidTargetApi (205) |
UCREUnsupportedOperationException (116) | AndroidXUiThread (195) |
AndroidXFloatRange (80) | AndroidXAnyThread(184) |
5.2. RQ2: Evolution
Table 11 presents the number of contracts in the first and second versions by category. In general, for most cases, the number of contracts in each category increased from the first to the last version. The only categories where the number decreased were the Apache’s Commons Validate and in JSR305 annotations package for Java. The decrease in JSR305 usage could be explained by it currently being in a dormant status, or in other words, with no activity since 2017.
contracts (1st vers.) | contracts (2nd vers.) | ||||
Type | category | Java | Kotlin | Java | Kotlin |
cond. runtime exc. | CRE | 10,678 | 1,161 | 14,887 | 2,071 |
unsupp. op. exc. | CRE | 203 | 26 | 308 | 116 |
java assert | assertion | 1,308 | - | 2,217 | - |
kotlin assert | assertion | - | 1,498 | - | 2,370 |
guava precond. | API | 677 | 1 | 1,121 | 9 |
commons validate | API | 11 | 0 | 3 | 0 |
spring assert | API | 0 | 0 | 1 | 0 |
JSR303, JSR349 | annotation | 0 | 0 | 0 | 0 |
JSR305 | annotation | 2,062 | 7 | 1,133 | 13 |
findbugs | annotation | 0 | 0 | 0 | 0 |
jetbrains | annotation | 714 | 40 | 1,596 | 98 |
android | annotation | 4,990 | 2,290 | 7,013 | 3,414 |
androidx | annotation | 53,721 | 6,782 | 86,212 | 13,811 |
kotlin contracts | other | - | 0 | - | 1 |
We computed some metrics to understand how the increase in the program’s size relates to the number of contracts. Those metrics, listed in Table 12, include the average and median values for the number of methods, the number of contracts, and the ratio between both (for the first and second versions). The table shows that there is an average increase of about 109.936 methods per program. This is expected since the program’s size tends to increase from the first to the second version. However, a more interesting insight comes from the contracts count. Although the average number of contracts per program increased, its median value decreased. This means that the dataset includes outliers with a significant rise in contract usage that considerably affected the average value. To confirm this data, we computed the ratio between the number of contracts and the number of methods for each version of a program. Then, we computed the difference between the second and the first version’s ratio for each program. The average of these differences is -0.0057, and the median is -0.0012. Although the values are very small, we conclude that the number of methods increases significantly more than the number of contracts.
1st version | 2nd version | |||
Metric | Median | Average | Median | Average |
methods count | 310 | 972,037 | 356 | 1,081,973 |
contracts count | 7 | 64,938 | 6 | 79,658 |
contract-to-method ratio | 0.029 | 0.061 | 0.022 | 0.055 |
Similarly to our study, Dietrich et al. (Dietrich et al., 2017) also found that the median value of the ratio does not change much. Still, while we reported a decline between both versions (0.029 to 0.022), they found a rise (0.021 to 0.023). This means that although both studies show general stability related to contracts usage, contrary to their study, we were not able to find a positive correlation between the increase in the number of methods and in the number of contracts.
5.3. RQ3: Safety
To address whether practitioners tend to misuse contracts in either program evolution or inheritance contexts, we build diff records to be classified according to evolution patterns. Some of these evolution patterns are associated with a potential risk that may lead to client breaks, namely when preconditions are strengthened or postconditions are weakened. This process was described in more detail in Sections 4.3.3 and 4.3.4. It is important to note that the analysis tool cannot precisely capture all contract changes due to the variety of constructs we are analyzing and the complexity of their semantics. This can potentially lead to under-reporting. Even so, Table 13 still provides valuable insights into the safety of contract usage and evolution. The table shows the frequency of each evolution pattern in the context of program evolution (third column). At first glance, we may be rushed to conclude that specifications are generally stable since the most frequent pattern is when a contract remains unchanged from the first to the second version. Unfortunately, this is not true since the occurrences of contract changes make up more than 50% of the patterns found. Still, overall, most of the changes are non-critical ones — including minor changes, preconditions weakening, and postconditions strengthening — which is a positive finding. Less optimistic is that the second most common pattern is the case of preconditions strengthening, one of the two cases that potentially offers risk. In summary, although many contracts remain unchanged and most changes are not critical, we still found many occurrences that can lead to potential breaks.
Finally, Table 13 also presents the results found for evolution patterns in the context of inheritance (fourth column). We observe that the preconditions strengthening pattern makes up almost 50% of classified instances. We also note that from the classified instances, most parts are related to contract changes which means a lack of stability in specifications. Both in the evolution and the inheritance study, we found low occurrences of postconditions weakening when compared to the other classifications. Also, compared to the reports from Dietrich et al.’s study (Dietrich et al., 2017), our results indicate a greater ratio of preconditions strengthening per preconditions found.
Contract Evolution | Critical | Evolution (#) | Inheritance (#) |
---|---|---|---|
unchanged | no | 32,070 | 158 |
minor change | no | 199 | 1 |
pre-conditions weakened | no | 13,870 | 3 |
post-conditions strengthened | no | 9,906 | 71 |
pre-conditions strengthened | yes | 20,870 | 232 |
post-conditions weakened | yes | 5,461 | 0 |
unclassified | ? | 8,307 | 145 |
6. Discussion
In this section, we answer the research questions listed in LABEL:sec:research-questions, we discuss the practical implications of our findings, and we outline potential threats to the validity of our work.
6.1. Answers to Research Questions
Given the findings reported in the previous section, we answer the research questions posed in LABEL:sec:research-questions as follows:
RQ1 [Contract Usage] How and to what extent are contracts used in Android applications? Contracts are concentrated in a small number of applications. Still, when applications use contracts, annotation-based approaches are the most frequent, with the androidx.annotation package being the most popular. The use of APIs to specify contracts is rare. This study found differences between the two languages. While in Java, 63.73% of the classified instances are preconditions, Kotlin programs display a more equally distributed selection with 38.82% postconditions at the top. We also found that more than 50% of the classified contracts in Java are related to null-checking, while in Kotlin that number is smaller than 10%.
RQ2 [Evolution] How does contract usage evolve in an application? Applications that use contracts continue to use them in later versions. When comparing the number of contracts in both versions of the applications, on average, the number of contracts increases. Still, this is caused by some outliers that increase its usage intensively, driving up the average. In fact, the median value decreases. Furthermore, the contract-to-method ratio decreases between versions — a median decrease of -0.0057 and an average decrease of -0.0012. Although by a residual factor, we observed that the number of contracts declines as the program grew.
RQ3 [Safety] Are contracts used safely in the context of program evolution and inheritance? We found that contract changes are frequent, meaning a lack of specifications’ stability. From those changes, preconditions strengthening is the most classified pattern. These results clearly show a potentially unsafe use of contracts that may lead to client breaks.
6.2. Practical Implications & Recommendations
From the findings presented in this work, we are able to derive practical implications and recommendations.
Recommendation 1: Due to the visible fragmentation of technologies and approaches to specifying contracts, both Java and Kotlin standard libraries should be equipped with specialized constructs to specify contracts and with proper official documentation.
Recommendation 2: It would be desirable to have libraries that standardize contract specifications in Java and Kotlin. Our results suggest that these libraries should be built around annotation-based contracts, given its popularity among practitioners. An annotation-based approach, where specifications are added to the program as metadata, is similar to Eiffel’s approach, where the assertions do not obfuscate the method’s implementation. This recommendation also applies to tool builders: given that the current use of APIs in Android development appears to be relatively low, analysis tools for Android that leverage contracts should prioritize annotations.
Recommendation 3: New tools to aid practitioners writing contracts would be very valuable. For example, the integration into IDEs of contract suggestion features supported by tools for invariant inference, such as Daikon (Ernst et al., 2007), could help increase practitioners’ use of contracts. Another contribution could be IDE and continuous integration plugins to detect contract violations in the context of program evolution and inheritance.
Recommendation 4: Our findings show that Kotlin’s default non-nullable types reduce the need to explicitly write some contracts, highlighting the significance of language design features that enable safety by default. These findings are relevant for the design of programming languages and can serve as motivation for practitioners when selecting programming languages for new projects.
6.3. Threats to Validity
Internal Validity. The accuracy of our results depends on the quality and correctness of the artifact and there may exist bugs in the code. To mitigate this, we extensively tested the tool. In addition, all code and datasets used are publicly available for other researchers and potential users to check the validity of the results. External Validity. The projects that we selected might not be an accurate representation of other, more popular, Android app stores. We mitigated this by using F-Droid, a collection of open-source applications commonly used in other research studies. We also mitigated this risk by analysing all the projects that satisfy the inclusion criteria, leading to a substantial dataset (51 MLoC) with applications of different types. Conclusion Validity. We might have missed language constructs that could be used to specify contracts. To mitigate this, we followed an established taxonomy (Dietrich et al., 2017) that we adapted and extended by systematically searching forums and the official Android documentation. Also, all our code is easily open to extension. Another risk comes from the fact that our dataset is imbalanced (with more Java than Kotlin applications). We mitigate this by explicitly discussing this imbalance when presenting results that might be affected by it.
7. Conclusions
Additional empirical evidence about what types of constructs and language features are used by practitioners to represent contracts can help the software engineering community create or improve existing libraries and tools to increase DbC adoption. This knowledge also serves practitioners to understand DbC’s current practices better, hel** them discover and decide between different implementation approaches of this technique for their projects. Other researchers can also use this data to draw additional studies and to foster further discussion along with the increasing interest in these empirical studies about language features.
Future work includes studies with practitioners to understand the challenges faced when specifying contracts, the use of annotations to improve Android analysis tools (Ribeiro et al., 2021; Pereira et al., 2022), and the development of tools that can help increase the adoption of DbC (Álvaro Silva et al., 2024).
References
- (1)
- A.Feldman et al. (2006) Y. A.Feldman, O. Barzilay, and S. Tyszberowicz. 2006. Jose: aspects for design by contract. In Fourth IEEE International Conference on Software Engineering and Formal Methods. Los Alamitos, CA, USA.
- Algarni and Magel (2018) A. Algarni and K. Magel. 2018. Toward Design-by-Contract Based Generative Tool for Object-Oriented System. In 2018 IEEE 9th International Conference on Software Engineering and Service Science (ICSESS). Proceedings. Piscataway, NJ, USA, 168 – 73.
- Aniche (2022) M. Aniche. 2022. Effective Software Testing. A Developer’s Guide. Manning, Shelter Islands.
- Backes et al. (2016) M. Backes, S. Bugiel, and E. Derr. 2016. Reliable Third-Party Library Detection in Android and Its Security Applications. In Proceedings of the 2016 ACM SIGSAC Conference on Computer and Communications Security (CCS ’16). Association for Computing Machinery, 356–367.
- Bloch (2008) Joshua Bloch. 2008. Effective java (2nd ed.). Addison-Wesley Professional.
- Blom et al. (2002a) M. Blom, E.J. Nordby, and A. Brunstrom. 2002a. An experimental evaluation of programming by contract. In Proceedings Ninth Annual IEEE International Conference and Workshop on the Engineering of Computer-Based Systems. 118–127.
- Blom et al. (2002b) M. Blom, E. J. Nordby, and A. Brunstrom. 2002b. On the relation between design contracts and errors: a software development strategy. In Proceedings Ninth Annual IEEE International Conference and Workshop on the Engineering of Computer-Based Systems. 110–117.
- Casalnuovo et al. (2015) C. Casalnuovo, P. Devanbu, A. Oliveira, V. Filkov, and B. Ray. 2015. Assert Use in GitHub Projects. In 2015 IEEE/ACM 37th IEEE International Conference on Software Engineering (ICSE). Proceedings, Vol. 1. Los Alamitos, CA, USA, 755 – 66.
- Chalin (2006) P. Chalin. 2006. Are practitioners writing contracts? Springer, Berlin, Germany, 100 – 113.
- Chen et al. (2019) Sen Chen, Lingling Fan, Chunyang Chen, Ting Su, Wenhe Li, Yang Liu, and Lihua Xu. 2019. Storydroid: Automated generation of storyboard for Android apps. In 2019 IEEE/ACM 41st International Conference on Software Engineering (ICSE). IEEE, 596–607.
- Counsell et al. (2017) S. Counsell, T. Hall, T. Shippey, D. Bowes, A. Tahir, and S. MacDonell. 2017. Assert Use and Defectiveness in Industrial Code. In Proceedings of the IEEE International Symposium on Software Reliability Engineering Workshops. 20–23.
- Dietrich et al. (2017) J. Dietrich, D. J. Pearce, K. Jezek, and P. Brada. 2017. Contracts in the Wild: A Study of Java Programs. In 31st European Conference on Object-Oriented Programming (ECOOP 2017) (Leibniz International Proceedings in Informatics (LIPIcs), Vol. 74). Schloss Dagstuhl–Leibniz-Zentrum fuer Informatik, Dagstuhl, Germany, 9:1–9:29.
- Ernst et al. (2007) Michael D Ernst, Jeff H Perkins, Philip J Guo, Stephen McCamant, Carlos Pacheco, Matthew S Tschantz, and Chen Xiao. 2007. The Daikon system for dynamic detection of likely invariants. Sci. Comput. Program. 69, 1-3 (2007), 35–45.
- Estler et al. (2014) H.-C. Estler, C. A. Furia, M. Nordio, M. Piccioni, and B. Meyer. 2014. Contracts in Practice. In FM 2014: Formal Methods. 19th International Symposium. Proceedings: LNCS 8442. Cham, Switzerland, 230 – 46.
- Fairbanks (2019) G. Fairbanks. 2019. Better code reviews with design by contract. IEEE Software 36, 6 (2019), 53 – 6.
- Grazia and Pradel (2022) L. Di Grazia and M. Pradel. 2022. The Evolution of Type Annotations in Python: An Empirical Study. In Proceedings of the 30th ACM Joint European Software Engineering Conference and Symposium on the Foundations of Software Engineering (Singapore, Singapore). Association for Computing Machinery, New York, NY, USA, 209–220.
- Hollunder et al. (2012) B. Hollunder, M. Herrmann, and A. Hülzenbecher. 2012. Design by Contract for Web Services: Architecture, Guidelines, and Map**s. In International Journal on Advances in Software, Vol. 5.
- Kochhar and Lo (2017) P. Kochhar and D. Lo. 2017. Revisiting Assert Use in GitHub Projects. In Proceedings of the 21st International Conference on Evaluation and Assessment in Software Engineering. 298–307.
- Kudrjavets et al. (2006) Gunnar Kudrjavets, Nachiappan Nagappan, and Thomas Ball. 2006. Assessing the Relationship between Software Assertions and Faults: An Empirical Investigation. In 2006 17th International Symposium on Software Reliability Engineering. 204–212.
- Meyer (1988) Bertrand Meyer. 1988. Programming as contracting. Advances in Object-Oriented Software Engineering (1988), 1–15.
- Meyer (1992) B. Meyer. 1992. Applying ‘design by contract’. Computer 25, 10 (1992), 40 – 51.
- Murthy (2018) P. V. R. Murthy. 2018. Design by contract methodology. In 2018 International Conference on Advances in Computing, Communications and Informatics (ICACCI). Piscataway, NJ, USA, 482 – 8.
- Naumchev (2019) A. Naumchev. 2019. Seamless Object-Oriented Requirements. In 2019 International Multi-Conference on Engineering, Computer and Information Sciences (SIBIRCON). Proceedings. Piscataway, NJ, USA.
- Pereira et al. (2022) Ricardo B Pereira, João F. Ferreira, Alexandra Mendes, and Rui Abreu. 2022. Extending Ecoandroid with automated detection of resource leaks. In Proceedings of the 9th IEEE/ACM International Conference on Mobile Software Engineering and Systems. 17–27.
- Ribeiro et al. (2021) Ana Ribeiro, João F. Ferreira, and Alexandra Mendes. 2021. Ecoandroid: An Android studio plugin for develo** energy-efficient Java mobile applications. In 2021 IEEE 21st International Conference on Software Quality, Reliability and Security (QRS). IEEE, 62–69.
- Schiller et al. (2014) T. W. Schiller, K. Donohue, F. Coward, and M. D. Ernst. 2014. Case Studies and Tools for Contract Specifications. In Proceedings of the 36th International Conference on Software Engineering (Hyderabad, India) (ICSE 2014). Association for Computing Machinery, New York, NY, USA, 596–607.
- Silva et al. (2020) C. Silva, S. Guerin, R. Mazo, and J. Champeau. 2020. Contract-based design patterns: a design by contract approach to specify security patterns. In ARES 2020: Proceedings of the 15th International Conference on Availability, Reliability and Security. New York, NY, USA.
- Stats (2023) StatCounter Global Stats. 2023. Operating System Market Share Worldwide. https://gs.statcounter.com/os-market-share#monthly-202208-202209-bar [Online; accessed 3-February-2023].
- Tantivongsathaporn and Stearns (2006) J. Tantivongsathaporn and D. Stearns. 2006. An experience with design by contract. In 2006 13th Asia Pacific Software Engineering Conference (APSEC’06). Piscataway, NJ, USA, 327 – 33.
- Tao and Edmunds (2018) K. Tao and P. Edmunds. 2018. Mobile APPs and Global Markets. Theoretical Economics Letters 08 (01 2018), 1510–1524.
- Wang et al. (2020) Y. Wang, B. Chen, K. Huang, B. Shi, C. Xu, X. Peng, Y. Wu, and Y. Liu. 2020. An Empirical Study of Usages, Updates and Risks of Third-Party Libraries in Java Projects. In 2020 IEEE International Conference on Software Maintenance and Evolution (ICSME). 35–45.
- Wei et al. (2011) Y. Wei, C.A. Furia, N. Kazmin, and B. Meyer. 2011. Inferring better contracts. In 2011 33rd International Conference on Software Engineering (ICSE 2011). Piscataway, NJ, USA, 191 – 200.
- Yu et al. (2021) Z. Yu, C. Bai, L. Seinturier, and M. Monperrus. 2021. Characterizing the Usage, Evolution and Impact of Java Annotations in Practice. IEEE Transactions on Software Engineering 47, 5 (2021), 969–986.
- Zeng et al. (2019) Yi Zeng, **fu Chen, Weiyi Shang, and Tse-Hsun Chen. 2019. Studying the characteristics of logging practices in mobile apps: a case study on f-droid. Empirical Software Engineering 24 (2019), 3394–3434.
- Zhou et al. (2017) Y. Zhou, P. Pelliccione, J. Haraldsson, and M. Islam. 2017. Improving Robustness of AUTOSAR Software Components with Design by Contract: A Study Within Volvo AB. In Software Engineering for Resilient Systems. 9th International Workshop, SERENE 2017. Proceedings: LNCS 10479. Cham, Switzerland, 151 – 68.
- Álvaro Silva et al. (2024) Álvaro Silva, Alexandra Mendes, and João F. Ferreira. 2024. Leveraging Large Language Models to Boost Dafny’s Developers Productivity. arXiv:2401.00963 [cs.SE]
Supplementary Material
The following appendices contain additional material that complements the main body of the paper.
Appendix A Information about Available Data / Artifacts
All the code and datasets are publicly available at https://figshare.com/s/d6eb7e5522bb535dc81a
Alternatively, the following links can also be used:
-
•
The code is available at: https://anonymous.4open.science/r/contracts-android-3E30
-
•
The dataset is available at: https://drive.google.com/file/d/1X8Qy3yamzjIZyc_h5AtNW84-91qFTiV4/view?usp=share_link
Appendix B Dataset: GitHub Statistics
Figure 1 shows the distribution of GitHub-related metrics — including the number of contributors, stars, watchers, and forks — for the projects that form the evaluated dataset. While the number of contributors describes the project’s team and its size, the number of stars, watchers, and forks help to assess each project’s popularity and relevance among other developers. For reference, the maximum outlier for each metric is 1682 for watchers, 33,689 for stars, 11,633 for forks, and 398 for contributors. Again, this diversity ensures the quality of the dataset and reduces potential bias.
Appendix C Contracts in Android Applications
C.1. CREs
An exception can be used to signal, at runtime, a contract violation. However, it is important to note that the exception itself does not represent a contract; it needs to be associated with a previous check — for example, an exception thrown inside an if-else block — to be considered so. Java and Kotlin offer many exceptions that can be used for this purpose, such as the IllegalArgumentException. The android.util package offers additional exceptions that we are also interested in analyzing, such as the case of the AndroidRuntimeException. We are also interested in a particular exception, the UnsupportedOperationException, which, according to the Java documentation, is thrown to indicate that the requested operation is not supported. An example of this is the following method proceedWithCheckout, which states that it can only perform its task when the shop**Cart has at least one item:
C.2. APIs
The methods provided by the Validate class are simply wrap** exceptions that we have already considered in the CREs. Still, as we can see in the following Kotlin code snippet, this API contributes to cleaner code compared to a raw CRE-based solution since we can specify the contracts in a single line and with meaningful wording. In particular, we specify a precondition stating that the items list is not empty:
Appendix D Algorithm for diff records of the contracts
The algorithm to create diff records is illustrated in Figure 2. The algorithm to create diff records consists of walking through each contract identified in the usage study (steps 1 and 2). We create a unique index for each contract in the loop to ensure we are not double-counting occurrences (steps 3 and 4). If the contract was not analyzed yet, we determine whether the contract belongs to the first version of the application (step 5). If this is the case, we create a diff record by retrieving all the contracts in both versions of this contract’s method (steps 5.a and 5.b). Otherwise, if the contract belongs to the last version of the application (step 6), we determine whether the associated method already existed in the first version (step 7). If the method existed and its first version did not contain contracts, we create a diff record with only the last version’s contracts (steps 8 and 9). If the first version contains contracts, we do not create a diff record to not double-count contracts since they will be reported by step 5.b. Ultimately, the program outputs the diff records created for each program-version method (step 10).
Appendix E Liskov Substitution Principle Study: Example
LABEL:lst:3-evolution-inheritance-1 shows the TagEntry class that extends the EntryItem class. It also overrides the setName method inherited from its parent class. Note that while the superclass implementation contains no contract, the subclass implementation adds a CRE precondition throwing an IllegalStateException when the id property does not end with the name parameter. Therefore, we are in the presence of a precondition strengthening in the context of inheritance, i.e., a violation of the Liskov Substitution Principle.
Appendix F Finding 4: Top 100 Applications
Regarding Finding 4, we have also looked into whether there was any relation between the number of contracts in the last version and any of the GitHub metrics from Figure 1. However, no meaningful correlation was found.
Appendix G Table 6 extended
In this section, we present a version of Table 6 (in the submitted paper) that also includes the number of applications (last two columns).
contracts (all ver.) | contracts (2nd ver.) | applications | |||||
Construct | Category | Java | Kotlin | Java | Kotlin | Java | Kotlin |
cond. runtime exc. | CRE | 25,565 | 3,232 | 14,887 | 2,071 | 779 | 285 |
unsupp. op. exc. | CRE | 511 | 142 | 308 | 116 | 97 | 27 |
java assert | assertion | 3,525 | - | 2,217 | - | 325 | - |
kotlin assert | assertion | - | 3,868 | - | 2,370 | - | 234 |
guava precond. | API | 1,798 | 10 | 1,121 | 9 | 22 | 4 |
commons validate | API | 148 | 0 | 3 | 0 | 1 | 0 |
spring assert | API | 1 | 0 | 1 | 0 | 1 | 0 |
JSR303, JSR349 | annotation | 0 | 0 | 0 | 0 | 0 | 0 |
JSR305 | annotation | 4,195 | 20 | 2,133 | 13 | 40 | 4 |
findbugs | annotation | 0 | 0 | 0 | 0 | 0 | 0 |
jetbrains | annotation | 2,310 | 138 | 1,596 | 98 | 115 | 20 |
android | annotation | 12,003 | 5,704 | 7,013 | 3,414 | 910 | 464 |
androidx | annotation | 139,933 | 20,593 | 86,212 | 13,811 | 599 | 401 |
kotlin contracts | others | - | 1 | - | 1 | - | 1 |
Appendix H List of Conditional Runtime Exceptions analyzed
CREs Constructs | |
AndroidRuntimeException | MissingResourceException |
ArithmeticException | NegativeArraySizeException |
ArrayStoreException | NoSuchElementException |
ArrayStoreException | NullPointerException |
BufferOverflowException | ParcelFormatException |
BufferUnderflowException | ParseException |
ClassCastException | ProviderException |
CompletionException | ProviderNotFoundException |
ConcurrentModificationException | RejectedExecutionException |
DOMException | SQLException |
DateTimeException | SecurityException |
EmptyStackException | TypeNotPresentException |
EnumConstantNotPresentException | UncheckedIOException |
FileSystemAlreadyExistsException | UndeclaredThrowableException |
FileSystemNotFoundException | UnsupportedOperationException |
IllegalArgumentException | WrongMethodTypeException |
IllegalMonitorStateException | AcceptPendingException |
IllegalStateException | AccessControlException |
IncompleteAnnotationException | AlreadyBoundException |
IndexOutOfBoundsException | AlreadyConnectedException |
LSException | ArrayIndexOutOfBoundsException |
MalformedParameterizedTypeException | BadParceableException |
MalformedParametersException | CancellationException |
UnsupportedAddressTypeException | UnsupportedCharsetException |
WritePendingException | ZoneRulesException |
CancelledKeyException | PatternSyntaxException |
ClosedDirectoryStreamException | StringIndexOutOfBoundsException |
ClosedFileSystemException | ReadOnlyBufferException |
ClosedFileSystemException | ReadOnlyFileSystemException |
ClosedSelectorException | ReadPendingException |
ClosedWatchServiceException | ShutdownChannelGroupException |
ConnectionPendingException | StringIndexOutOfBoundsException |
NonReadableChannelException | UnknownFormatConversionException |
NonWritableChannelException | UnknownFormatFlagsException |
NotYetBoundException | UnresolvedAddressException |
NotYetConnectedException | UnsupportedTemporalTypeException |
NumberFormatException | Overlap**FileLockException |
Appendix I List of API’s methods analyzed
Annotations analyzed | |
Apache lang2 Validate | |
allElementsOfType() | |
isTrue() | |
noNullElements() | |
notEmpty() | |
notNull() | |
Apache lang3 Validate | |
allElementsOfType() | |
exclusiveBetween() | |
inclusiveBetween() | |
assignableFrom() | |
isInstanceOf() | |
matchesPattern() | |
notBlank() | |
validIndex() | |
validState() | |
Guava Preconditions | |
checkArgument() | |
checkState() | |
checkElementIndex() | |
checkPositionIndex() | |
checkNotNull() | |
checkPositionIndexes() | |
Spring Assert | |
doesNotContain() | |
hasLength() | |
hasText() | |
notEmpty() | |
noNullElements() | |
isInstanceOf() | |
isAssignable() | |
state() | |
isNull() | |
isTrue() | |
notNull() |
Appendix J List of Annotations analyzed
Annotations analyzed | ||
JSR305 |
||
@CheckForNull | @CheckForSigned | |
@MatchesPattern | @Nonnegative | |
@Nonnull | @Nullable | |
@OverridingMethodsMustInvokeSupper | @ParametersAreNonnullByDefault | |
@RegEx | @Signed | |
@Syntax | @Syntax | |
@Tainted | @Untainted | |
@WillClose | @WillCloseWhenClosed | |
@WillNotClose | @Guardedby | |
@Immutable | @NotThreadSafe | |
@ThreadSafe | ||
JSR303, JSR349 |
||
@Null | @DecimalMin | |
@NotNull | @Size | |
@AssertTrue | @Digits | |
@AssertFalse | @Past | |
@Min | @Future | |
@Max | @Pattern | |
@DecimalMax | ||
JetBrain |
||
@Contract | @NotNull | |
@Nullable | @PropertyKey | |
@TestOnly | ||
IntelliJ |
||
@BoxLayoutAxis | @CalendarMonth | |
@CursorType | @FlowLayoutAlignment | |
@FontStyle | @HorizontalAlignment | |
@InputEventMask | @ListSelectionMode | |
@PatternFlags | @TabLayoutPolicy | |
@AdjustableOrientation | @Flow | |
@Identifier | @TabPlacement | |
@TitledBorderJustification | @TitledBorderTitlePosition | |
@Language | @MagicConstant | |
@Pattern | @PrintFormat | |
@PrintFormat | @RexExp | |
@Subst | ||
FindBugs |
||
@CheckForNull | @NonNull | |
@Nullable | @PossiblyNull | |
@FontStyle | @HorizontalAlignment | |
@UnkownNullness | @CreateObligation | |
@DischargesObligation | @CleanupObligation | |
Android |
||
@AndroidSupressLint | @AndroidTargetApi | |
Androidx |
||
@AnimatorRes | @AnimRes | |
@AnyRes | @AnyThread | |
@AnyThread | @ArrayRes | |
@AttrRes | @BinderThread | |
@BinderThread | @BoolRes | |
@CallSuper | @CheckResult | |
@ChecksSdkIntAtLeast | @ColorInt | |
@ColorLong | @ColorRes | |
@ContentView | @DimenRes | |
@Dimension | @NotInline | |
@DrawableRes | @FloatRange | |
@FloatRange | @FontRes | |
@FontRes | @FractionRes | |
@FractionRes | @GuardedBy | |
@GuardedBy | @HalfFloat | |
@IdRes | @InspectableProperty | |
@IntDef | @IntegerRes | |
@InterpolatorRes | @IntRange | |
@Keep | @LayoutRes | |
@LongDef | @MainThread | |
@MainThread | @MenuRes | |
@NavigationRes | @NonNull | |
@Nullable | @PluralsRec | |
@Px | @RawRes | |
@RequiresApi | @RequiresFeature | |
@RequiresPermission | @RestrictTo | |
@Size | @StringDef | |
@StringRes | @StyleableRes | |
@StyleRes | @TransitionRes | |
@UiThread | @VisibleForTesting | |
@WorkerThread | @XmlRes |