Objects.requireNonNull(some);
final A a = some.getA();
Objects.requireNonNull(a, "explanation");
validate(a);
final B b = a.getB();
Objects.requireNonNull(b, "explanation");
b.process(param1);
Same code written using glue:
some..ensureNotNull()//
.getA()..ensureNotNull("explanation")..ensureValid()//
.getB()..ensureNotNull("explanation")..process(param1..ensureNotNull()); -- easy
MOTIVATION
While many of the core benefits of glue classes (such as method attachment, composability, and disciplined null handling) could be simulated with static utility methods — the practical outcome is fundamentally different and hard to compare with glue classes.
Most code bases I've used (and contributed to) are full of sprawling utility modules, overloaded with:
- Configurable options and sys-opts
- Redundant null-checking and validation
- A continuous tension between usability and maximum usable decomposition
- Decomposition makes compact APIs hard to discover:
Static methods live outside the type they operate on. Even with clever tags or code-rewriting, you can't naturally find, autocomplete, or chain them as you would with instance (or "glue") methods. - Responsibility separation increases friction:
The more you split up code as recommended ("don't pollute domain objects, keep util logic separate"), the less obvious it is to the next developer where to look for required behaviour. - Null-handling becomes boilerplate:
The majority of library util methods guard against null upfront—resulting in repeated validation, fat method signatures, or the spread of verbose Optional-driven patterns(force us to rewrite known code just for null from time to time).
- Discoverability and fluency:
By attaching methods as views directly to types, glue classes make contextually appropriate utilities instantly available and visible right where they're needed. - Controlled extension and evolution:
Behavioural changes, versioning, and testing remain isolated and explicit; you can swap, layer, or upgrade glue classes without altering core types or writing brittle adapters, I would compare it to default methods that are not limited by class ownership and not-null state. - Centralized, composable null policies:
You can bake robust, contextual null-handling exactly once, in glue, and chain safely—even for null receivers. This way code could be decomposed without negative consequences. - Cleaner architecture without trade-off:
Code remains decomposed, modular, and maintainable, yet the surface API is obvious — giving the best of both worlds.
While static utilities, annotations, and dynamic tooling can go a long way to simulate some extension patterns, only glue classes offer a truly fruitful, disciplined, and developer-friendly solution for modular extension, composable integration, and safe evolution—unlocking readable, discoverable, and maintainable APIs at scale that would work for multiple disconnected teams - basically you can see it as Kotlin’s extension functions on steroids.
PATH TO GLUE
However, these features suffer from important limitations:
- C# - Member methods always win
- C# - Same name among extension will cause compiler error
- Kotlin - Member functions always win
- Kotlin - Same name among extension you get a compiler ambiguity or you can rename it in import
- You can’t distingush member vs extension syntactically, so you also can’t tell which ones could be written to accept null. Because "member always wins", adding a new member later can silently change which function gets called, including changing null behavior. They allow discoverability from IDE viewpoint - but they scale with each method.
In 2009 I tried bring up concept that had none of those problems. It's under: 2009-March (Glue classes proposal 0.9) - because of it's rudimentally form I don't recommend reading it just yet.
Below you will finding Non-Java syntax that is not particularly necessary and could be changed to other form, but was introduced to make understanding of new concept more intuitive:
- .. - static member selection operator
- G[] this - this used as variable name
- public static <G> void glue(Iterator<G> this) - sample anchor method for IDE
- extends ..GA, ..GB - multi-inheritance of static methods
Glue classes are special classes that allow us to add methods to any classes, records or types, potentially value classes maybe primitives as well, as though we were "gluing" new methods onto them. Unlike extension functions, they’re designed to be systematically discoverable. Instead of manually searching or importing functions, the IDE could automatically know which glue classes can apply. They can use natural class hierarchies to avoid name collisions.
Lets follow a practical example how this concept would change Java the one where we supposedly want to add methods to any array:
public class ArrayGlue{
// this method would carry metadata for discovery
public final static <G> void glue(G[] this){ /* empty */ }
// -1 in extractByIndexes uses null
public static <G> G[] extractByIndexes(G[] this, int... indexes) { /* ... */ }
public static <G> G[] setOn(G[] this, int index, G value) { /* ... */ }
}
String [] record = ...;
String [] identification = ArrayGlue.extractByIndexes(record, 0, 2, 3, -1);
ArrayGlue.setOn(identification, 3, param1);
import static ArrayGlue.*:
String [] record = ...;
String [] identification = extractByIndexes(record, 0, 2, 3, -1);
setOn(identification, 3, param1);
- when used at start it's ensures that only imported static methods are considered while matching methods
- when used as connection it injects the variable, expresion that is on the left as the first parameter
So first we would get secure against possibility of new methods with same name comming to live:
import static ArrayGlue.*:
String [] record = ...;
String [] identification = ..extractByIndexes(record, 0, 2, 3, -1);
..setOn(identification, 3, param1);
import static ArrayGlue.*:
String [] record = ...;
String [] identification = record..extractByIndexes(0, 2, 3, -1);
identification..setOn(3, param1);
This could be further shortened to:
import static ArrayGlue.*:
String [] record = ...;
String [] identification
= record
..extractByIndexes(0, 2, 3, -1)
..setOn(3, param1);
This could be considered an alternative to:
- Java Language Enhancement: Disallow access to static members via object references
Now lets focus on:
public static <G> void glue(G[] this){ /* empty */ }Glue classes as any other can be extended to create more complex utilities:
public class ArrayFastGlue extends ArrayGlue { // or ..ArrayGlue
public static <G> void glue(G[] this){ /* empty */ }
public static <G> G[] extractByIndexes(G[] this, int... indexes) { /* ... */ }
}
public class IteratorGlue {
public static <G> void glue(Iterator<G> this){}
public static <G, R> Iterator<R> map(Iterator<G> this, Function<? super G, ? extends R> fn) {
// Implementation: returns an Iterator<R> that applies fn to each element of original Iterator<G>
}
}
Iterator<String> it = ...;
Iterator<Integer> numbers = it..map(String::length);
Following Java namespace ruless we could be more precise and use longer names:
Iterator<Integer> numbers = it..IteratorGlue.map(String::length);
Iterator<Integer> numbers = it..package.IteratorGlue.map(String::length);
map(it, String::length);
IteratorGlue.map(it, String::length);
package.IteratorGlue.map(it, String::length);
For adding new methods to exisintg ones we could use simple inheritance:
public class ArrayFastGlue extends ..ArrayGlue{
public static <G> G[] extractByIndexes(G[] this, int... indexes) { /* ... */ }
}
What's more, exclusively for glue classes, we could allow multiple inheritance restricted to static-only methods — further increasing flexibility and quality. The following rules would apply: If two (or more) parent glue classes define static methods with the same signature, the child glue class MUST explicitly re-declare these methods (with the option to delegate to a parent method if desired). Only the most-derived (child/last) glue class in a given inheritance hierarchy is discoverable for extension methods. When a variable is checked for glue extension, the IDE (or compiler) only considers the last glue class in the hierarchy. Methods inherited from parent glue classes are available only if they have not been redeclared (overridden) in the child class. This both prevents method ambiguity and ensures intentional API design.
public class ArrayComplexGlue extends ..ArrayFastGlue, ..ArrayApacheGlue{
public static <G> void glue(G[] this){ /* empty */ } // need to be restaed to eliminate collision
public static <G> G[] collision(G[] this){
return this..ArrayApacheGlue.collision();
}
// Marking already existing method as depeciated
@Deprecated
public static <G> G[] unefficient(G[] this){
return this..ArrayApacheGlue.unefficient();
}
}
This would make ideal solution for everyday use, but it would still make the classes that are globally used cluttered or force developers to use really bad names to prevent collisions - to solve this problem we could add custom domain spaces (mutable, immutable, efficient, secure, archaic, integration, ... or self-domain like o for object ): To make this happen we would need to exclude classes that start with lowercase character from import and exploration by default (probably for glue classes only) or make it at default skip inner/sub glue classes (if written then it would still work);
This way if we want to extend class with methods and we can categorise them by spaces then we have really pretty alternative to bad naming convention:
public class ArrayGlue{
public final static <G> void glue(G[] this){ /* empty */ }
public static <G> G[] setOn(G[] this, int index, G value) { /* ... */ }
public static <G> G[] copyWithSetOn(G[] this, int index, G value) { /* ... */ }
}
public class ArrayGlue{
public final static <G> void glue(G[] this){ /* empty */ }
public static class immutable{
public final static <G> void glue(G[] this){ /* empty */ }
public static <G> G[] setOn(G[] this, int index, G value) { /* copy is made */ }
}
public static class mutable{
public final static <G> void glue(G[] this){ /* empty */ }
public static <G> G[] setOn(G[] this, int index, G value) { /* given array is used */ }
}
}
this way code:
String[] record = ...;
record = record..copyWithSetOn(1, "~");
String[] record = ...;
record = record..immutable.setOn(1, "~");
import static pkg.ArrayGlue.*; should contrary to current compiler behaviour import subspace glue clases. This would be deliberate incompatible with current Java.
import glue pkg.ArrayGlue;
import pkg.ArrayGlue;
import static pkg.ArrayGlue.*;
import static pkg.ArrayGlue.immutable;
import static pkg.ArrayGlue.mutable;
OR glue subclasses should be treated as regular members and become available through standard imports, without any additional semantic transformations — this would be the better option in my opinion!
Resolving glue methods requires a resolution limiter. After transforming
record..copyWithSetOn(1, "~"); ..copyWithSetOn(record, 1, "~"); // .. is here 'only static methods filter' Same as it's now discoverability would be handled by IDE and compilation would be determined by import.
What IDEs Do Now (Standard Java)
When you type ., the IDE:
- Looks up the static type at the cursor location.
- Fetches all visible methods from the class, all its superclasses, and all implemented interfaces.
- Maybe also adds static imports, inherited generic methods, and overrides.
- This process is fast because:
- The class/method hierarchy is well-known, fixed, and heavily indexed/cached by the IDE.
- There are relatively few methods per type (typically in the dozens, rarely more than a few hundred even in very complex hierarchies).
The IDE would need to build an indexes:
- GlueIndex[] - for certain match
- InterfaceA -> [GlueA, GlueB, ...]
- ArrayList -> [GlueC]
- Map -> [GlueMapUtils, ...]
- Object -> [ObjectGlue]
- ...
- PotentialGlueIndex[] - for potential match
- InterfaceA -> [InterfaceAGlue, ...]
- ...
- one more if we allow more complex syntax/li>
User types foo., IDE additionally to classic completions gets the static type of foo.
- Looks up direct match in glue indexes.
- Optionally traverses the inheritance/superinterface tree.
- Apply filtering if needed
- Quickly gets all matching glue methods for suggestion.
Sample placement in index
- ? extends Foo >> GlueIndex[Foo]
- ? super Foo >> at the top level should not be allowed as it do not give any particullar usability or could be placed in GlueIndex[Object]
- G extends InterfaceA & InterfaceB >> GlueIndex[InterfaceA] or GlueIndex[InterfaceB]
- A wildcard bound like ? extends that appears inside a generic type is recorded only as a constraint used later during filtering, not as the primary key in the index.
- A receiver parameter declared as ? extends InterfaceA is indexed under InterfaceA.
- For a type parameter declared as T extends InterfaceA & InterfaceB, it does not matter which of the interfaces is used as the primary indexing key, because any valid T must implement both. Discovery based on one bound will still find the glue, and a subsequent filtering step will verify that the second bound is also satisfied.
- Glue classes inherit the same erasure limitations static methods already have today.
- Discovery is based on one type vs all method signature - and it's limiting factor as well.
Fast code completion: Indexed lookups are fast; filtering is cheap for non-complex hierarchies.
Scalable for project or module scope:
The cost of glue-based completion/discovery grows linearly in the number of glue classes that are applicable to the type in question. In other words:
- For a given type G, if there are k glue classes that apply to G, then lookup is O(k).
- Adding one more glue for G turns this into O(k+1); so the complexity grows proportionally with the number of glues relevant to G, not with the total size of the project or classpath.
- Futher more with effort we could limit it to O(1)
..ensureNotNull(); // ObjectGlue
..call(); // FooMod1Glue
..FooMod1Glue.call();
..call(); // FooMod2Glue
..FooMod2Glue.call();
Collisions between independent glue classes:
// Library X
public class XArrayGlue {
public static <G> void glue(G[] this) {}
public static <G> G[] map(G[] this, Function<G,G> fn) { ... }
}
// Library Y
public class YArrayGlue {
public static <G> void glue(G[] this) {}
public static <G> G[] map(G[] this, Function<G,G> fn) { ... }
}
import XArrayGlue;
import XArrayGlue.*;
arr..map(...); // OK because only XArrayGlue is visible for compiler
import XArrayGlue;
import XArrayGlue.*;
import YArrayGlue;
import YArrayGlue.*;
arr..map(...) // ERROR: ambiguity
arr..XArrayGlue.map(...) // OK
arr..YArrayGlue.map(...) // OK
PARTIAL GENERICS
Java can only get all in or all out, while it should be possible to selectively infer generic types, this way, the one of many that we actualy want different or compiler could not infer could be specified.
Bad but usefull example:
public static <K, V> Map<K, V> listToMapFrame(List<K> keys) {...} Map<String, Integer> m = Maps.<String, Integer> listToMapFrame(List.of("a", "b", "c")); Map<String, Integer> m = Maps.<String, ?? > listToMapFrame(List.of("a", "b", "c"));
public class IteratorGlue {
public static <G> void glue(Iterator<G> this){}
public static <G, R> Iterator<R> map(Iterator<G> this, Function<? super G, ? extends R> fn) {
// Implementation: returns an Iterator<R> that applies fn to each element of original Iterator<G>
}
}
Iterator<String> it = ...;
Iterator<Integer> numbers = it..package.IteratorGlue.map(String::length);
so when needed we would be able to write/ just as now we are not required (in most cases) to redeclare class generic types:
Iterator<Integer> numbers = it..package.IteratorGlue.<Integer>map(String::length);
// under glue we would have <[G,] R> so both <R> and full <G, R> could be used
decoded to:
Iterator<Integer> numbers = ..package.IteratorGlue.<??, Integer>map(it, String::length);
Iterator<Integer> numbers = it..package.IteratorGlue.<String,Integer>map(String::length);
LAST TOUCHES
..glueMthods()..ensureNotNull().classicMethods();
class DTO{
private String name;
public glue void setName(String name){
if (this==null){ return; }
this.name = name;
}
public glue String getName(){
if (this==null){ return null; }
return this.name;
}
}
For this reason
if (this == null) { return; } - this - is a conceptual receiver parameter for glue method
- this is never null at runtime, so
this == nullandthis != nullis dead code and can be removed/optimized by the compiler/JIT. - it's exact reason why this is used in glue samples
FINAL STEP
public class ArrayGlue{
// this method would carry metadata for discovery
public final static <G> void glue(G[] this){ /* empty */ }
// -1 in extractByIndexes uses null
public static <G> G[] extractByIndexes(G[] this, int... indexes) { /* ... */ }
public static <G> G[] setOn(G[] this, int index, G value) { /* ... */ }
}
public glue class ArrayGlue<G> glue(G[]){
// -1 in extractByIndexes uses null
public static G[] extractByIndexes(int... indexes) { /* ... */ }
public static G[] setOn(int index, G value) { /* ... */ }
}
OVERVIEW
Glue classes(methods) introduce a language-level mechanism allowing developers to add methods to existing types without modifying their source code, breaking encapsulation, or incurring runtime overhead. Using '..' to call static methods. Glue classes provide type-safe, modular, and discoverable extension capabilities, formalizing patterns typically handled by utility classes, extension methods, or reflection-based frameworks.
At the same time inner Glue classes & methods would allow to keep gains where private access is needed.
- Bindings: The .. operator binds identically to the . operator in terms of precedence and associativity, but differs in semantics: the left-hand expression is evaluated once and passed as first argument, and instead routes the receiver value to a statically resolved glue method.
- Discoverability:All glue methods applicable to a type are always visible for discover. For compilation import are required making glue methods match deterministic.
- Attach methods to any class, interface, array, or type parameter explicitly.
- Access follow standard Java rules.
- Fully static, compile-time resolution: No runtime cost, reflection, proxies, or bytecode tricks.
- Inheritance-based conflict resolution: Only imported glue classes are available for compilation. If both base and derived glue classes are imported(unnecessary), the derived (subclass) glue will take precedence.
- Explicit import and qualification: Only imported glue classes are available for resolution, preventing accidental API pollution.
- Invocable on null receivers: Glue methods can be designed to handle null, enabling centralized and fluent null-handling.
- Module and JPMS friendly: Glue classes fit into Java’s module system, enforcing clean integration and export boundaries.
Clean, type-safe, and modular extension of existing APIs! Glue classes solve the problem of utility/god classes, domain pollution, and ambiguous extension by enabling explicit, discoverable, and testable augmentation of types without touching their source or relying on runtime mechanisms.
- Separation of Core and Utility Logic: Keeps domain classes clean; convenience, formatting, mapping, and integration logic are moved to glue classes.
- First-Class Integration and Mapping: Supports explicit, safe cross-module converters and bridges without inheritance tangles or reflection frameworks.
- Better Discoverability and Readability: Glue methods appear as instance-like methods in IDEs, making APIs easier to learn, read, and maintain.
- Centralized, Fluent Null Handling: Glue can define null policies; methods can be called on null receivers, supporting robust pipeline-style code.
- Safe API Evolution and Versioning: Allows new glue versions to coexist; inheritance and import resolve conflicts clearly and compile-time safely.
- Testing Isolation: Glue methods are stateless and separate, making isolated testing easier and less error-prone.
- Architectural Clarity: APIs and modules are kept clean; glue methods are never accidentally leaked across modules.
- Ability to Override or Replace Legacy Utility Methods: Glue classes allow to fix, optimize, mark as deprecated, override, fully replace outdated, unsafe, or redundant utility methods without changing the original utility class or domain model. This also enables remediation of API debt and architectural inconsistencies in external libraries or legacy modules—all through explicit, compile-time safe glue mechanisms, not invasive source edits or risky runtime hooks.
- Generated Classes & Records & value classes: Natural targets for glue class extensions.
- Lambdas: Glue chaining is fully compatible with lambdas, example:
values..iterate() //
..filterEach(t -> t != null) //
..mapEach(t -> t < 3 ? -t : t) //
..collectToArray(Integer[].class);
- Requires language, compiler, and IDE changes.
- New syntax and type resolution rules: Developers must learn and adapt to glue concepts.
- More complex collision resolution than standard utilities or extensions.
- Static utility/helper classes (e.g., *Utils, *Helper): Frequent in Java, often lead to "God" classes and poor discoverability.
- Extension methods (C#, Kotlin): Partial coverage; limited discoverability, poor collision/versioning management, little module clarity.
- Reflection-based frameworks and bytecode weaving: Dynamic but fragile; runtime overhead, ambiguity, hard to reason about and test.
- Optional wrappers and null-patterns: Verbose, contagious throughout codebase, break fluent APIs, often make code bloated.
EXAMPLES
String[] dict = { /* your string data */ };
// Step 1:
List<List<String>> groups = new ArrayList<>();
boolean[] used = new boolean[dict.length];
for (int i = 0; i < dict.length; i++) {
if (used[i]) continue;
List<String> group = new ArrayList<>();
char key = dict[i].charAt(0);
for (int j = 0; j < dict.length; j++) {
if (!used[j] && dict[j].charAt(0) == key) {
group.add(dict[j]);
used[j] = true;
}
}
groups.add(group);
}
// Step 2:
List<String[]> mappedGroups = new ArrayList<>();
for (List<String> group : groups) {
int n = group.size();
if (n <= 1) {
mappedGroups.add(group.toArray(new String[0]));
} else {
String[] arr = new String[4];
arr[0] = group.get(0);
arr[1] = "~";
arr[2] = group.get(n-1);
arr[3] = "";
mappedGroups.add(arr);
}
}
// Step 3:
StringBuilder builder = new StringBuilder();
for (int i = 0; i < mappedGroups.size(); i++) {
String[] arr = mappedGroups.get(i);
if (i > 0) {
builder.append(", ");
}
builder.append(String.join(" ", arr));
}
// If you want the result as a String:
String printout = builder.toString();
// Step 1: Group by first character connection
// Step 2: Map/Transform each group
// Step 3: Flatten
String[] dict = ...;
String printout = dict
..iterateByConnection((a, b) -> a..firstChar()..equals(b..firstChar())) //
..map(t -> t..size() <= 1 //
? t //
: t..extractByIndexes(0, -1, t.length-1, -1)..setOn(1, "~") //
) //
..collectToArray(String[].class) //
..joinArrays(", ", " ");
public class ObjectGlue<T> glue(T) {
public glue boolean equals(this, T other) { /* ... */ }
}
public class StringGlue glue(String) {
public glue Character firstChar(this) { /* ... */ }
}
public class ArrayGlue<T> glue(T[]) {
public glue Iterator<T[]> iterateByConnection(this, BiPredicate<T, T> connection) { /* ... */ }
public glue int size(this) { /* ... */ }
// -1 in extractByIndexes uses null
public glue T[] extractByIndexes(this, int... indexes) { /* ... */ }
public glue T[] setOn(this, int index, T value) { /* ... */ }
public glue String joinArrays(this, String... /* [dimension] */ delimiters) { /* ... */ }
}
public class IteratorGlue<T> glue(Iterator<T>) {
public glue T[] collectToArray(this, Class<T> componentType) { /* ... */ }
public glue <R> Iterator<R> map(this, Function<? super T, ? extends R> fn) { /* ... */ }
}
public class
IteratorGlue<T> glue (Iterator<T>) {
↓ ↓
public glue < R> Iterator<R> map( this, Function<? super T, ? extends R> fn) { /* ... */ }
=
public static <T, R> Iterator<R> map( Iterator<T> this, Function<? super T, ? extends R> fn)
{ /* ... */ }
public class Outer {
private int value;
public static class ValueGlue glue(Outer) {
public glue int doubleValue(this) {
return this!=null ? this.value * 2 : 0; // has access to private field of Outer
}
}
}
public class Storage {
private String name;
public glue String getName(){
return this!=null? this.name: null;
}
}
// could be transformed to:
public class Storage {
private String name;
public String getName() {
return this != null ? this.name : null;
}
public static glue class Glue glue(Storage) {
public glue String getName(this) {
return this != null ? this.name : null;
}
}
}
// making both calls possible:
storage.getName();
storage..getName();
public class MessageGlue glue(Message) {
private static final SimpleDateFormat FORMAT = new SimpleDateFormat("yyyy-MM-dd");
public glue String formatDate(this) {
// Not thread safe! Shared FORMAT instance
return FORMAT.format(this.getDate());
}
}
public class MessageGlueThreadSafe glue(Message) extends MessageGlue {
@Override
public glue String formatDate(this) {
if (this==null) return null;
// Thread-safe alternative with same format
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
return this.getDate().toInstant() //
.atZone(ZoneId.systemDefault()) //
.toLocalDate() //
.format(formatter);
}
}
public glue class ArrayGlue<T> glue(T[]) {
// core glue methods (iterate, size, extractByIndexes, ...)
public static glue class immutable<T> glue(T[]) {
public glue T[] reversed(this) { /* always creates a new array */ }
public glue T[] setOn(this, int index, T value) { /* copy and set */ }
}
public static glue class mutable<T> glue(T[]) {
public glue void clear(this) { /* ... */ }
public glue String[] reverse(this) { /* ... */ }
public glue T[] setOn(this, int index, T value) { /* in-place set */ }
}
}
public glue class ArrayGlueGen2<T> extends ArrayGlue<T> glue(T[]) {
public static class immutable<T> extends ArrayGlue.immutable<T> glue(T[]) {
public glue T[] reversed(this) { /* optimized: skip copy for length <= 1 */ }
}
}
Standard, Default Import (Preferred)
import ArrayGlue;
new String[][]{{}}//
..mutable.reverse()
..mutable.setOn(3, new String[] {...})
..joinArraysToSting(" | ", ",");
Importing Newer/Derived Glue (Inheritance/Shadowing)
import ArrayGlue; // WARNING: You are using an older glue, ArrayGlueGen2 is available
import ArrayGlueGen2;
new String[][]{{}}//
..mutable.reverse()
..mutable.setOn(3, new String[] {...})
..joinArraysToSting(" | ", ",");
Importing Spaces
import ArrayGlue.immutable; // Allowed, but discouraged (warning)
import ArrayGlue.mutable; // Allowed, but discouraged (warning)
import ArrayGlue; // Brings all top-level + inner glue into scope
new String[][]{{}}//
..reverse()
..setOn(3, new String[] {...}); // <-- Compile error: 'setOn' name collision (ambiguous because it come from both immutable and mutable)
DETAILS
GlueClassDeclaration
ClassModifiersopt class Identifier TypeParametersopt glue ( GlueType ) [ extends ..SuperGlueClass (, ..SuperGlueClassB) ] GlueClassBody
GlueMethodHeader:
MethodModifiersopt glue TypeParameters*opt ResultType GlueMethodDeclarator Throwsopt
GlueMethodDeclarator:
Identifier ( GlueType this, FormalParameterListopt )
GlueDualMethodDeclarator: // compiled to classic and glue (inside generated InnerGlueClass)
Identifier (FormalParameterListopt )
GlueMethodInvocation:
variable..glueMethod(...)
variable..GlueClass.glueMethod(...)
variable..gluePath.GlueClass.glueMethod(...)
variable..<TypeArg>glueMethod(...)
variable..<GlueClassTypeArg><TypeArg>glueMethod(...)
GlueDualMethodHeader:
MethodModifiersopt glue TypeParametersopt ResultType Identifier ( FormalParameterListopt )
// desugared to an instance method + a static glue method with 'this' inserted
The original type is not physically modified or extended in any way by glue classes; neither instance nor static shape of the type is changed. Rather, when glue methods are invoked, the compiler transforms these calls into static method calls on the relevant glue class, passing the target ("receiver") as the first argument. Additionally, when creating outer glue the original type is preserved as-is in the scope —no glue methods are added directly — so any attempt to access protected or private members, or to override internal implementation, is impossible. This guarantees encapsulation, prevents accidental API leakage across modules, and avoids the complexities and hidden bugs of runtime weaving or bytecode patching. All glue logic remains explicit, statically checked, and safely separated, enforcing consistent usage and clear architectural boundaries. At same time inner glue classes follow standard Java ruless and have access to private fields.
Summary:
- Additional metadata may/will be needed by the compiler to resolve glue method linking at compile-time (private method could be used for it).
- The type is preserved unmodified at the bytecode and source level.
- Glue usage is enforced at compile-time—all calls are routed via explicit glue classes/methods.
- Attempting to access glue methods directly on the type (without glue import) will result in a compile-time error.
- The risk of name collision, encapsulation break, or accidental glue import after removing a glue statement/import - greatly dimished, thereby supporting robust and maintainable code evolution.
- All glue methods remain usable as classic static methods, providing backwards compatibility and migration flexibility.
Like simple static methods vs new sematics would give same results.
COMPATIBILITY
CLARIFICATIONS
- Null Policy for Glue Methods: When invoking a glue method on a potentially-null receiver, the method should by convention check for null and return null directly, skipping further logic. As null-checkings are easily enforced using
..ensureNotNull()when needed - this way there would be no need to create each method twice, one for non-null and the second for null. - Conflict/Override Resolution:Glue method name conflicts can be resolved by qualifying the invocation with the GlueClassName, for example, obj..MyGlueClass.method(). If a glue class is extended, standard Java inheritance and import precedence rules apply—meaning that a subclassed glue class can override methods of its predecessor, just as with regular Java static methods.
Remember, at the compilation and invocation level, glue methods are just static methods in their glue classes. Thus, all static method resolution rules, import qualifying, and inheritance behaviors apply exactly as they do for ordinary Java static methods. There is no ambiguity introduced by glue; it remains as modular and predictable as standard Java. - Methods Alone Could Be Enough: In theory, most glue functionality could be achieved with plain static utility methods that are written to accept the receiver as the first argument (e.g., <TX> ensureNotNull(T o)). In principle, even signatures like <TX> ensureNotNull(T(this) o) could express this pattern and allow extension-style calls.
However, resolving applicable methods for a given type, especially across large codebases, would overwhelm both human readers and IDE tooling. Discoverability, auto-completion, and intent become difficult to manage when extension logic is dispersed across many static methods without any structuring or explicit association to the original type. Glue classes, by contrast, organize extension methods in a type-centric and module-aware way, ensuring that IDEs, analysis tools, and developers can easily locate and reason about available extensions.. - Proposed Collision Policy: Glue method name collision: If multiple glue classes provide a method of the same name (e.g. ensureNotNull) for the same target type and both are in scope, it is a compile-time error.
POSSIBLE EXCEPTION: If any (or all but one) of the colliding methods are marked as @Deprecated, the non-deprecated method is chosen transparently, or you force users to migrate by requiring they call the non-colliding name.
Deprecation + Delegation: You may create a new, better-named method in a new glue class. Old glue class’s method is marked @Deprecated and delegates to the new one or provides upgrade guidance in its javadoc/message. - Multiple Inheritance: Ideally we could make glue classes extend multiple glue classes with same glue type.
- Partial generic type "filling": Filling types need to be complete:
public static <T, X> X method(T t, x) {...}- both X and Y need to be present in calssic static methods or none, but glue classes give us possibility to make only local part filled. - Spaces: We could extend import rules to make lower name class-es non importable by default(could be forced) - this way we could create spaces for logic
class StringGlues{ public class fast glue(String){ public glue int hash(){...}} }would allow to more precise executions:"SomeString"..fast.hash();. In this case sapce import would be performed by outer glue immport (similar to .*). - Migration/Interoperability: IDE and build tools must be glue-aware for this transition to be smooth
- Expectation: Glue classes/methods inherit all generic features and erasure behavior from static methods. The only addition is the clean, type-centric invocation syntax and modularity.
- Battle: A modern, well-specified "glue" mechanism would help keep Java the go-to language for large, safe, evolving platforms. Not adopting such a feature likely means falling behind in the expressivity and maintainability race — something other popular languages will quickly capitalize on.
- Method Reference Syntax: under the hood static code would work the same for method references.
- Behavioral Consistency:
dict..iterateByConnection(...)..map(...)..ifNull(otherMap..ensureNotNull()).toString();- When chaining expressions where any step may yield null, glue methods such as ifNull(...) allow you to seamlessly fall back to a non-null value, ensuring subsequent method calls can proceed safely and fluently. This enables robust, readable null-handling in extension method pipelines. - Chain ambiguity: With long chains, it can be unclear what the current context is. However, glue methods such as ..ensureNotNull() or ..ensureType(Class<T>) make this easy to resolve. If you need to guarantee that a value is not null, simply call ..ensureNotNull(); if you need to validate its type then ..ensureType(Class<T>) clarifies intent directly in the chain. This approach gives you the freedom to make your code as precise and robust as necessary. Additional gain would be avoiding cluttering your scope with temporary variables that are often redundant or, in many cases, difficult to remove from context / sometimes harmful.
- Dual-method generation: A method declared with glue - inside a normal class is not a normal instance method, but a dual-declared construct that produces:
a classic instance method (where this is guaranteed non-null), and
a glue static method (where this is an explicit parameter and may be null)
This way we can prevent code duplication. - Possibility of delegating glue to instance method: It's wrong path as we lose control over null-controll if we would always resolve glue to glueMethod(this){ this!=null ? this.method() : null; } .
- Recommendation:
Use.map()for operations on the whole object or when the collection changes type/form.
Use.mapEach()for per-element transformations, making intent clear. - Best Practice: Use Context to Guide Null Strategy:
..ensureNotNull(Runnable)- Nulls might translate into user warnings or default field values.
- In some cases null are natural.
- Unclarity: Dual-method support is a possible future evolution only. It does not change how existing, non-glue classes behave in the core proposal.
- Dual-methods: In case of doubt, the first step should be to analyze the behavior of identical static methods.
- this vs null: The preferred convention is that glue methods are null-safe by default (they should tolerate a null receiver and handle it gracefully), but this is never enforced. Some glue methods may legitimately require non-null receivers and can assume or enforce non-null where appropriate.
- Glue class syntax variants: Multiple glue class syntaxes are shown intentionally to illustrate different possible final surface forms and to aid understanding for readers with different backgrounds. They all represent the same underlying concept; a real specification would choose a single concrete syntax.
- Partial generics (??): The ?? notation is underspecified and not valid Java syntax. It should be treated as a conceptual or future-looking idea, not part of the concrete glue proposal.
CONTROL TAKEOVER OPERATOR
AS POSSIBLE PATH OF EVOLUTION
>>>. This operator enables developers to inject control-flow or observability logic inline within glue method chains, using a concise block syntax such as >>> { ... local ... } where local represents current value in chain.
At any point in a chain, a developer can insert a control takeover block:
v = value
.stepOne() >>> { if (isLast(local)) { break local; } } //
..stepTwo() >>> { if (!valid(local)) { return null; } } //
..stepThree() >>> { log( local ) } //
..stepFor() ;
L local = null;
localLabel:
{
// Step 1: first glue operation + optional early exit
S step1 = GlueClass.stepOne(value);
if (isLast(local)) { local = step1; break localLabel; };
// Step 2: second glue operation + optional return
S step2 = GlueClass.stepTwo(step1);
if (!valid(local)) { return null; }
// Step 3: final glue operation
S step3 = GlueClass.stepThree(step2);
log(step3);
S step4 = GlueClass.stepThree(step3);
// Assign result
local = step4;
}
- Side effects (logging, metrics, customized debugging)
- Condition checks (for validation, auditing, or transformation)
- Control flow takeover Using statements like return ...; - to immediately exit method
or break local; to break the chain and return a specific value.
- Readability: Keeps all logic linear and transparent, avoiding deeply nested structures or scattered utility calls.
- Power: Enables both tap/peek actions and in-chain early exits (short-circuiting), which classic chains in Java cannot naturally perform.
- Safety & Modularity: Ensures that side-effects and bail-outs are explicit and localized, minimizing risk of bugs and increasing code maintainability.
- Flexibility for Evolution: Glue chains become even more expressive, supporting robust patterns for validation, recovery, dynamic branching, and auditing—without giving up the clarity of idiomatic Java.
Using { ... local ... } as the block structure fits perfectly with glue’s philosophy of "extending but not modifying."
- Developers can operate on local without needing to introduce new variable names or disrupt the data flow.
- The block’s scope is contained, making side-effects or exit points obvious to reviewers and maintainers.
FORMATTER BARRIER
In the examples, a trailing line comment (//) is occasionally used as a formater barrier to prevent automated formaters or IDEs from collapsing or reflowing long fluent chains. This convention has been used successfully in production codebases for more than decade, including in large and continuously evolving systems, without causing semantic issues or tooling problems. Its primary benefit is preserving the visual structure of code across edits and refactoring, which significantly improves readability, code review quality, and long-term maintainability; it also helps reviewers more easily identify flawed logic or misunderstandings during code review. Maintaining a stable visual layout supports developers—especially those who rely on visual patterns when reading and reasoning about code—in recognizing intent, spotting inconsistencies, and retaining structural understanding even after substantial changes. This practice affects only formatting, has no impact on compilation or runtime behavior, and is not part of the glue language design itself; however, without it, much of the readability the glue concept aims to provide would be diminished.
Tools already treats comments as layout anchors!
Just compare:
public static <D extends IcdCodeGet & Comparable<D>, L extends IcdListAccess & Comparable<L>> IcdCodeGet[] getBestCodes( //
ComparableList<ComparableLink<L, IcdCodeGet[]>> bests //
, L list //
, boolean renew //
, ExtendedIterator<CounterCmp<D>> statsSource) {...}
public static <D extends IcdCodeGet & Comparable<D>, L extends IcdListAccess & Comparable<L>> IcdCodeGet[] getBestCodes( ComparableList<ComparableLink<L, IcdCodeGet[]>> bests, L list, boolean renew, ExtendedIterator<CounterCmp<D>> statsSource) {...}
AI-Generated Code Era
AI-generated code can be highly variable in style, often verbose, and sometimes makes poor organizational/architectural choices.
Glue classes create clear, idiomatic extension points: utility logic, mapping, and integration become much easier for humans (and tools) to discover, review, and maintain.
Glue classes enforce separation of concerns. Instead of AI inserting more static utility methods or extending random core types, you get explicit, type-safe "views" on types.
This prevents "utility God-class hell" or accidental core pollution or both.
AI-generated patches or refactors can’t easily disrupt core logic if extension happens only via glue.
Obsolete logic in "bad" glue can be deprecated and shadowed without source edits.
It’s easier to audit and check changes:
Is this a glue class or a core-domain change?
What glue modules are imported?
What extension logic has this AI contributed?
Reviewers know that outer glue class code can only touch public APIs, making risk estimation and test planning easier.
Rather than hundreds of slightly different static helpers, glue classes let you group, version, and document extension logic by intention and context.
AI tools and IDEs can auto-suggest glue methods as completions on types, making the interface surface area predictable and discoverable.
Glue imports and inheritance mean you can choose which "spaces" (project, module, library) are active, reducing confusion.
As both code and glue evolve—especially with rapid AI-generated iteration—controlled glue means you don’t pollute the method namespace or lose track of which parts of your codebase are being extended or changed.
0 comments / komentarz(y):
Post a Comment