Home > database >  How to create generic method returning different types extending the same parent
How to create generic method returning different types extending the same parent

Time:08-03

I have created method like this:

public BaseMenu openMenu(MenuItem menuItem) {
        utils.clickOnElement(menuItem);
        BaseMenu baseMenu = null;

        switch (menuItem) {
            case FIRST:
                baseMenu = new FirstMenu();
                break;
            case SECOND:
                baseMenu = new SecondMenu();
                break;
            case THIRD:
                baseMenu = new ThirdMenu();
                break;
        }
        return baseMenu;
    }

It returns type BaseMenu. How to convert it, so that it would return specific menu types (which are extending BaseMenu), like FirstMenu, SecondMenu, etc.?

I tried something like that:

public <T extends BaseMenu> T openMenu(MenuItem menuItem) {
        utils.clickOnElement(menuItem);
        T baseMenu = null;

        switch (menuItem) {
            case FIRST:
                baseMenu = (T) new FirstMenu();
                break;
            case SECOND:
                baseMenu = (T) new SecondMenu();
                break;
            case THIRD:
                baseMenu = (T) new ThirdMenu();
                break;
        }
        return baseMenu;
    }

but it also returned BaseMenu.

How should code look like to work as I am expecting? Is it possible to do it this way? Is there any other way to achieve it?

CodePudding user response:

It is not possible to convert this function. Returning BaseMenu was a valid solution which described your use case. It should be the caller's responsibility to cast it to the required type if needed.

The main idea of a generic you let the caller determine the type of the generic so your function can focus on the parts you really cares about. So if I call that function and state that I want that generic to be a class I implemented myself, your function will need to use it.

For example, this code sample would be a valid usage of the generic version.

class Foo extends BaseMenu { /* etc. */ }

Foo foo = openMenu(menuItem);

Alternatively, it may make more sense if you also add methods for firstMenu, secondMenu, etc. You should really only be selecting the menu this way if you only need to use functionality all BaseMenus implement.

CodePudding user response:

public <T extends BaseMenu> T openMenu(MenuItem menuItem) {
    utils.clickOnElement(menuItem);
    T baseMenu = null;
    
    switch (menuItem) {
        case FIRST:
            baseMenu = new FirstMenu();
            break;
        case SECOND:
            baseMenu = new SecondMenu();
            break;
        case THIRD:
            baseMenu = new ThirdMenu();
            break;
    }
    return (T) baseMenu;
}

when you call this method! you must use like this:

FirstMenu fm = openMenu(menuItem);

you should not use :

BaseMenu bm = openMenu(menuItem);

cause your method return value is T, that T type is what you handle that return value, if you use BaseMenu to take that return value, that T will be BaseMenu!!

  • Related