问题
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
T
that will be passed intofoo(T t)
will be an instance of eitherSomeB
orSomeC
, whereSomeB extends SomeA
andSomeC extends someA
.Both
SomeB
andSomeC
contains a methodglop(...)
which returnsSomeD
, however,glop(...)
is not inSomeA
.I want to be able to pass
foo(T t)
around so that it accepts eitherSomeB
orSomeC
, 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 ofSomeA
to worry about for this purpose, which seems reasonable given thatSomeA
doesn't haveglop()
.In the implementation of
foo()
, use theinstanceof
operator (orClass.isInstance()
) to recognize instances ofSomeB
andSomeC
, 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