问题
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
The variable
Tthat will be passed intofoo(T t)will be an instance of eitherSomeBorSomeC, whereSomeB extends SomeAandSomeC extends someA.Both
SomeBandSomeCcontains a methodglop(...)which returnsSomeD, however,glop(...)is not inSomeA.I want to be able to pass
foo(T t)around so that it accepts eitherSomeBorSomeC, however, I need to callglop(...)(e.g follows)
E.g.
public < T extends SomeA > String foo(T t) {
return t.glop().methodInsideOfSomeD().toString();
}
I would rather not implement foo separately for SomeB and SomeC (i.e. 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?
回答1:
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:
Replace the one
foo()with two overloaded ones, one whose type parameter is bounded bySomeB, and another whose type parameter is bounded bySomeC. This assumes that you have no other subtypes ofSomeAto worry about for this purpose, which seems reasonable given thatSomeAdoesn't haveglop().In the implementation of
foo(), use theinstanceofoperator (orClass.isInstance()) to recognize instances ofSomeBandSomeC, and handle them appropriately.Use reflection in
foo()to determine whether the argument's class provides an accessibleglop()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.
回答2:
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);
来源:https://stackoverflow.com/questions/41578147/calling-method-on-generic-type-java-8