Instantiate a generic class T object and return it

I'm working on java selenium tests and I am trying to setup a fluent/method-chaining design code:

I have a generic button class that allows navigating from a page class to the other. Buttons are attributes of my pages classes. Method in the button class should be able to return generic page classes that were defined on button instantiation (the page you are on, the page the button leads to etc.). But I can't figure how to do it without breaking the fluent design in the main class:

Button.java

public class Button<T, U> {
    public T observe() {
        return new T(); // Does not work
    }

    public U click() {
        return new U(); // Does not work
    }
}

FirstPage.java

public class FirstPage {
    public Button<FirstPage, SecondPage> buttonOnFirstPage = new Button<>();
}

SecondPage.java

public class SecondPage {
    public Button<SecondPage, AnotherPage> buttonOnSecondPage = new Button<>();
}

And finally what I want to be able to do without casting or mentioning types, in order to benefit from autocompletion and other fancy IDE stuff: BrowsingTest.java

public class BrowsingTest {
    public static void main(String[] args) {
        new FirstPage()
            .buttonOnFirstPage.observe()
            .buttonOnFirstPage.click()
            .buttonOnSecondPage.observe()
            .buttonOnSecondPage.click(); //etc.
    }
}

Any idea on how I can build that button class? Thanks a lot!

2 answers

  • answered 2020-07-29 17:38 VGR

    Generic types are a compile-time notation for ensuring type safety. They are erased at runtime.

    This means T and U do not exist at runtime. Which is why you can’t instantiate them.

    You can, however, pass in the constructors yourself:

    public class Button<T, U> {
        private final Supplier<? extends T> tConstructor;
        private final Supplier<? extends U> uConstructor;
    
        public Button(Supplier<? extends T> tConstructor,
                      Supplier<? extends U> uConstructor) {
    
            this.tConstructor = tConstructor;
            this.uConstructor = uConstructor;
        }
    
        public T observe() {
            return tConstructor.get();
        }
    
        public U click() {
            return uConstructor.get();
        }
    }
    

    And you can pass those constructors as method references:

    public class FirstPage {
        public Button<FirstPage, SecondPage> buttonOnFirstPage =
            new Button<>(FirstPage::new, SecondPage::new);
    }
    

  • answered 2020-07-29 18:00 StinkyPointer

    It's not possible to create an instance out of generic type you should look for these workarounds instead: Instantiating object of type parameter