4

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(...) (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?

Pankti
  • 411
  • 4
  • 13
  • 1
    Can you add `glop` to `SomeA`? – Elliott Frisch Jan 10 '17 at 20:52
  • 1
    Or, can you create an interface `CanGlop` with a default implementation of `glop()` and have `SomeB` and `SomeC` implement `CanGlop` as well as derive from `SomeA`? – Jim Garrison Jan 10 '17 at 20:53
  • Yes, you're out of luck. Either create two methods, or make an interface Glopable, and create adapter classes that implement Glopable by delegating to a SomeB or a SomeC. – JB Nizet Jan 10 '17 at 20:59
  • I cannot touch `SomeA`, `SomeB`, or `SomeC`. They exist in another library that I have no control over. So in my project, I'm literally just grabbing a `SomeB` or `SomeC`, and in their library they are extensions of `SomeA`. I don't believe I can change `SomeA`, `SomeB`, or `SomeC` unless I can touch their source, can I? – george_curious Jan 10 '17 at 20:59
  • You're basically out of luck. However depending on the circumstance you might be able to solve your problem using a facade: make an interface `Wrapper` that exposes all the methods you need from `SomeA` as well as `glop()` and then make two implementations, one that takes a `SomeB` on the constructor and delegates all calls to it, and another that does the same for `SomeC`. Then when you get a `SomeB` or `SomeC`, wrap it with the appropriate wrapper, after which you can write code in terms of `Wrapper`. – jacobm Jan 10 '17 at 21:12
  • Why not did you try a captcha? – Roman C Jan 10 '17 at 21:58

2 Answers2

2

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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
1

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);
NoDataFound
  • 11,381
  • 33
  • 59