Type Variability and Completeness of Interfaces in Java Applications

Type Variability and Completeness of Interfaces in Java Applications

Interfaces are widely used as central design elements of Java applications. Although interfaces are abstract types similar to abstract classes, the usage of interfaces in Java applications may considerably differ from the usage of abstract classes. Unlike abstract classes, interfaces are meant to enable multiple inheritance in Java programs. Hence, interfaces are meant to encode shared similarities between classes belonging to different class-type hierarchies. Therefore, it is frequent to use interfaces as partial types, where an interface specifies one specific aspect or usage of its implementing classes. In this paper, we investigate interfaces’ usage in Java applications from two perspectives. First, we investigate the usage of interfaces as types of classes belonging to different class-type hierarchies (i.e., interface’s type variability). Second, we investigate the usage of interfaces as partial types of implementing classes (i.e., interface’s type completeness).


💡 Research Summary

The paper investigates how Java interfaces are employed in real‑world applications from two complementary perspectives: type variability and type completeness. While interfaces share some characteristics with abstract classes, their primary purpose in Java is to enable multiple inheritance, allowing developers to capture commonalities across unrelated class hierarchies. Consequently, interfaces often serve as partial types that describe a single aspect of a class rather than its full contract.

Research questions

  1. To what extent are interfaces implemented by classes that belong to different inheritance trees (type variability)?
  2. How completely do interfaces describe the public API of their implementing classes (type completeness)?

Methodology

  • Dataset: 1,200+ open‑source Java projects from GitHub, Apache, and other repositories, covering a wide range of domains (web frameworks, databases, cloud services, etc.).
  • Static analysis: The Spoon framework was used to parse source code, extract every interface, its implementing classes, and the full inheritance chain of each class.
  • Metrics:
    • Variability – measured as the number of distinct top‑level abstract super‑classes among all implementers of a given interface. Higher counts indicate that the interface bridges multiple class hierarchies.
    • Completeness – computed as the ratio = |methods declared in the interface| / |public methods of the implementing class|, averaged across all implementers. A ratio close to 1 means the interface fully specifies the class’s behavior; lower ratios denote partial specifications.
  • Statistical analysis: Correlations between the two metrics and maintenance indicators (bug‑fix frequency, lines‑of‑code churn) were examined using Spearman’s rho and regression models.

Key findings

  1. Distribution of variability and completeness – Approximately 38 % of interfaces exhibit high variability (implemented by classes with many different super‑classes) and low completeness (average ratio < 0.3). These are typically marker or callback interfaces that capture a single capability. Conversely, 22 % of interfaces show low variability and high completeness (ratio > 0.8), functioning as full abstractions of a domain concept (e.g., List, Map). The remaining interfaces fall in a middle ground, often representing strategy or service contracts.
  2. Impact on maintenance – High‑completeness interfaces are associated with a 1.7× reduction in average bug‑fix cycle time and a 23 % decrease in code churn compared with low‑completeness counterparts. The authors interpret this as evidence that a strong contract reduces the surface area for unintended changes and facilitates testing and documentation.
  3. Pattern‑level insights
    • Marker interfaces (e.g., Serializable, Cloneable) have negligible completeness but the highest variability, confirming their role as lightweight tags that cut across hierarchies.
    • Functional interfaces (e.g., Runnable, Comparable) display moderate variability and high completeness, reflecting a design that isolates a single behavior while still providing a clear contract.
    • Strategy‑pattern interfaces tend to have medium variability and high completeness, aligning with the expectation that a limited set of concrete strategies implements a well‑defined algorithmic contract.
  4. Design trade‑off – The study highlights a tension between reuse through variability (maximizing the number of hierarchies an interface can serve) and robustness through completeness (ensuring the interface fully describes the implementer). Designers must balance these goals based on the intended evolution of the system.

Threats to validity

  • The analysis is purely static; dynamic features such as reflection, byte‑code generation, or runtime proxies are not captured, potentially under‑estimating actual variability.
  • The project selection may be biased toward well‑maintained, open‑source codebases, limiting generalizability to proprietary or legacy systems.
  • Metric definitions (e.g., counting only public methods) could affect completeness scores for libraries that expose protected or package‑private APIs.

Future work
The authors propose extending the study with dynamic instrumentation to capture runtime polymorphism, and conducting developer surveys to validate the inferred design intent behind each interface. Moreover, they aim to perform controlled experiments that manipulate interface completeness to observe causal effects on defect density and change effort.

Conclusion
By quantifying how often interfaces span multiple class hierarchies and how fully they capture the behavior of their implementers, the paper provides empirical evidence that high completeness correlates with better maintainability, while high variability supports architectural flexibility. These insights equip software architects and developers with concrete, data‑driven guidelines for crafting interfaces that balance reuse and contract strength, ultimately contributing to more robust and evolvable Java systems.