Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to return interface from generic class implementing the interface?

Tags:

java

generics

I want to make a method that accepts any class T that implements any interface I.

Then do something with the class and return the interface I that is implemented.

Here's what I've tried:

class MyLibrary {
    
    public static <I, T extends I> I registerImplementation(Class<T> classImpl) {
        I interfaceImpl = (I) classImpl.newInstance();
        return interfaceImpl;
    }
}

I'm then creating an interface and a class which implements that interface:

interface UserInterface {
    void doSomethingDefined();
}

class UserClass_v1_10_R2 implements UserInterface {

    @Override
    public void doSomethingDefined() {
        System.out.println("What this does is well-defined");
    }

    public void doVersionSpecificStuff() {
        System.out.println("This is not in the interface, so don't really depend on this");
    }
}

However, when I'm calling the method referencing UserClass, it returns the same class type T instead of the interface type I, allowing all the class methods which are not declared in the interface to be called.

I.e. in the statement below, even though registerImplementation() is declared to return a reference to UserInterface, the compiler sees the reference as pointing to an instance of UserClass_v1_10_R2, allowing access to the implementation's methods that are not in the interface.

MyLibrary.registerImplementation(UserClass_v1_10_R2.class).doVersionSpecificStuff();

Contrast this with the supposedly identical

UserInterface uiObj = MyLibrary.registerImplementation(UserClass_v1_10_R2.class);

uiObj.doVersionSpecificStuff(); // This does not compile
like image 941
user7401478 Avatar asked Nov 28 '20 20:11

user7401478


People also ask

Can a generic class implement an interface?

Only generic classes can implement generic interfaces. Normal classes can't implement generic interfaces.

Can we return interface from method?

Since an interface provides all of that, you can call methods on it, just as you can on a regular class. Of course, in order for the method to actually return some object, there needs to be some class that implements that interface somewhere.

How do you declare a generic interface?

Declaring Variant Generic Interfaces You can declare variant generic interfaces by using the in and out keywords for generic type parameters. ref , in , and out parameters in C# cannot be variant. Value types also do not support variance. You can declare a generic type parameter covariant by using the out keyword.


1 Answers

That's just how the compiler infers the type. If you compile with a special (undocumented) flag:

javac --debug=verboseResolution=all  ...

you will see that:

  Note: Deferred instantiation of method <I,T>registerImplementation(Class<T>)
  MyLibrary.registerImplementation(UserClass_v1_10_R2.class).doVersionSpecificStuff();
                                    ^
  instantiated signature: (Class<UserClass_v1_10_R2>)UserClass_v1_10_R2

Look at the instantiated signature: (Class<UserClass_v1_10_R2>)UserClass_v1_10_R2

So you need to add another argument, to take the interface:

public static <I, T extends I> I registerImplementation(Class<I> interfaceClass, Class<T> implClass) {
    // do whatever checks you want if this is an interface or if a constructor can be called....
    I interfaceImpl = null;
    try {
        interfaceImpl = interfaceClass.cast(implClass.newInstance());
    } catch (Exception e) {
        e.printStackTrace();
    }
    return interfaceImpl;
}

Of course this raises the question if you have multiple interfaces, but that is something for you to think about.

like image 60
Eugene Avatar answered Oct 23 '22 23:10

Eugene