Home  Programming   Fluent api ...

Fluent API and Generics with example

Look at the class ChromiumOptions at https://github.com/SeleniumHQ/selenium/blob/e8745c61e3ab3d1873d1a8cb178fc166aed63e71/dotnet/src/webdriver/Chromium/ChromiumOptions.cs

The use of generics in the declaration public class ChromiumOptions<T extends ChromiumOptions<?>> allows for a more flexible and type-safe implementation of the ChromiumOptions class. Let's delve into the details of why T is used and what would happen if it were not used.

Detailed Explanation

1. Generics in Java

Generics provide a way to parameterize types, making code more flexible and type-safe. The T in ChromiumOptions<T extends ChromiumOptions<?>> is a generic type parameter.

2. Fluent API and Method Chaining

The primary reason for using T extends ChromiumOptions<?> is to support a fluent API, which relies on method chaining. The fluent API pattern allows methods to return the current instance (this), so multiple method calls can be chained together in a readable manner.

Example with Generics

Consider the following method in the ChromiumOptions class:

public class ChromiumOptions<T extends ChromiumOptions<?>> extends AbstractDriverOptions<ChromiumOptions<?>> {
    // Other fields and methods

    public T setBinary(String path) {
        this.binary = path;
        return (T) this;
    }

    public T addArgument(String argument) {
        this.args.add(argument);
        return (T) this;
    }

    // Other methods
}

Here, T allows the methods to return the current instance of ChromiumOptions or any subclass thereof. This ensures that the return type matches the actual type of the object, supporting fluent chaining.

Why T extends ChromiumOptions<?>?

The T extends ChromiumOptions<?> constraint ensures that T is a subtype of ChromiumOptions<?>. This allows methods to return T instead of ChromiumOptions<?>, enabling fluent API chaining that is type-safe.

What Happens Without T

If we remove the generic T, the methods would return ChromiumOptions<?> or just ChromiumOptions. This would restrict the ability to use fluent method chaining in subclasses or in cases where a more specific type is required.

Example Without Generics

public class ChromiumOptions extends AbstractDriverOptions<ChromiumOptions> {
    // Other fields and methods

    public ChromiumOptions setBinary(String path) {
        this.binary = path;
        return this;
    }

    public ChromiumOptions addArgument(String argument) {
        this.args.add(argument);
        return this;
    }

    // Other methods
}

In this case, the methods return ChromiumOptions, which means the fluent API will only work for the ChromiumOptions class and not for any subclasses that might want to extend ChromiumOptions.

Subclass Example

Consider a subclass ChromeOptions that extends ChromiumOptions:

public class ChromeOptions extends ChromiumOptions<ChromeOptions> {
    // Additional Chrome-specific methods
}

With generics:

ChromeOptions options = new ChromeOptions()
    .setBinary("/path/to/chrome")
    .addArgument("--headless");

Without generics, the subclass would lose the ability to return its own type, breaking the fluent API:

public class ChromeOptions extends ChromiumOptions {
    // Additional Chrome-specific methods
}

// Usage without generics
ChromeOptions options = new ChromeOptions()
    .setBinary("/path/to/chrome")  // Returns ChromiumOptions, not ChromeOptions
    .addArgument("--headless");    // Further method calls won't be recognized on ChromeOptions
Published on: Jun 27, 2024, 04:52 AM  
 

Comments

Add your comment