简体   繁体   中英

Calling Method on Generic Type Java 8

I have a function that accepts a parameter that has some generic type

public < T extends SomeA > String foo(T t) {...}

Now... SomeA contains a series of methods that can be called, like baz1() , baz2() , baz3() , etc...

Now, here's the rub

  1. The variable T that will be passed into foo(T t) will be an instance of either SomeB or SomeC , where SomeB extends SomeA and SomeC extends someA .

  2. Both SomeB and SomeC contains a method glop(...) which returns SomeD , however , glop(...) is not in SomeA .

  3. I want to be able to pass foo(T t) around so that it accepts either SomeB or SomeC , however, I need to call glop(...) (eg follows)

Eg

public < T extends SomeA > String foo(T t) {
    return t.glop().methodInsideOfSomeD().toString();
}

I would rather not implement foo separately for SomeB and SomeC (ie fooSomeB fooSomeC ). I want to be able to pass this method around to functions providing either SomeB or SomeC (or even better anything that contains the method glop(...) ).

The problem : I do not have the ability to change the implementation or declaration of SomeB or SomeC since they reside in libraries I do not control. Thus, I cannot have them both implement some interface that provides glop(...) .

Am I just out of luck?

Note that there is pretty much no point to defining a generic method with the signature you describe. If the type parameter T is used only as the type of one or more arguments, then you might as well simplify things by manually erasing it:

public String foo(SomeA t) { /* ... */ }

In any case, you have at least these alternatives consistent with your requirements:

  1. Replace the one foo() with two overloaded ones, one whose type parameter is bounded by SomeB , and another whose type parameter is bounded by SomeC . This assumes that you have no other subtypes of SomeA to worry about for this purpose, which seems reasonable given that SomeA doesn't have glop() .

  2. In the implementation of foo() , use the instanceof operator (or Class.isInstance() ) to recognize instances of SomeB and SomeC , and handle them appropriately.

  3. Use reflection in foo() to determine whether the argument's class provides an accessible glop() having the expected signature, and if so, invoke it reflectively.

None of these require you to modify SomeA , SomeB , or SomeC in any way. I know you said you don't want to do (1), but it's pretty clean and easy. You should consider it.

I want to be able to pass this method around to functions providing [...] or even better anything that contains the method glop(...)).

That would be (3), maybe with the argument type changed to Object , but I'm doubtful that you really want that. Reflection is messy, and usually it should be avoided.

For what I understood, you will have to duplicate the method, but you may reuse the code by factorizing the common parts:

public < T extends SomeB > String foo(T t) {return fooX(t.glop());}
public < T extends SomeC > String foo(T t) {return fooX(t.glop());}

And a new method taking the result of glop() :

private String fooX(SomeD globResult) {
  return globResult.methodInsideOfSomeD().toString();
}

I don't know why you can't change SomeA to add that glob() method with a default implementation (or method), but if you can add an interface to your SomeB / SomeC class, say ProviderOfSomeD , you can do that:

public < T extends SomeA & ProviderOfSomeD> String foo(T t) {return fooX(t.glop());}

Notice there is a limit to what you can do with that.

Another Java 8 way (does not require any change):

public String foo(final Supplier<? extends SomeD> t) {return fooX(t.get());}

// calling them:
SomeB b = ...;
SomeC b = ...;
foo(b::glop);
foo(c::glop);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM