Struct Embedding in Java

Java supports composition through nested classes and interfaces, which can be used to express a more seamless composition of types. This is somewhat similar to struct embedding in other languages.

import java.util.function.Supplier;

class Base {
    int num;

    Base(int num) {
        this.num = num;
    }

    String describe() {
        return String.format("base with num=%d", num);
    }
}

class Container {
    private Base base;
    String str;

    Container(Base base, String str) {
        this.base = base;
        this.str = str;
    }

    // Delegate method to access base's num
    int getNum() {
        return base.num;
    }

    // Delegate method to access base's describe
    String describe() {
        return base.describe();
    }
}

public class StructEmbedding {
    public static void main(String[] args) {
        // When creating objects, we need to initialize the composed object explicitly
        Container co = new Container(new Base(1), "some name");

        // We can access the base's fields through getter methods on Container
        System.out.printf("co={num: %d, str: %s}%n", co.getNum(), co.str);

        // We can also access the full path using the getter method
        System.out.println("also num: " + co.getNum());

        // We can invoke a method that was delegated from Base directly on co
        System.out.println("describe: " + co.describe());

        // In Java, we can use interfaces to achieve a similar effect to embedding
        Supplier<String> describer = co::describe;
        System.out.println("describer: " + describer.get());
    }
}

To run the program:

$ javac StructEmbedding.java
$ java StructEmbedding
co={num: 1, str: some name}
also num: 1
describe: base with num=1
describer: base with num=1

In this Java example, we’ve simulated struct embedding using composition and delegation. The Container class contains a Base object and delegates some methods to it. This achieves a similar effect to struct embedding in other languages.

We’ve also used a functional interface (Supplier) to demonstrate how Java can achieve a similar effect to interface implementation through embedding. The Container object can be used wherever a Supplier<String> is expected, as long as we’re referring to its describe method.

While Java doesn’t have direct struct embedding, these techniques of composition, delegation, and interfaces can often achieve similar goals of code reuse and type relationships.