views:

306

answers:

7

Basically what I want, istwo public methods with slightly different return values to call the same method to do whatever work is needed. They both return the return value of the private method, however the private method will know how to return the correct value depending on the public method that called it.

Example methods:

public Map<Type1, Type3> doSomething1();
public Map<Type2, Type3> doSomething2();

private Map<Type1/Type2, Type3> doSomething();

So, in the above example, doSomething() returns either Type1 or Type2 as the Map's key-type, depending on which public method called it. It would be able to perform a simple check, and populate the map with objects of the correct type.

Maybe this can be done through some clever Java reflection? I'm not sure. It all seems pretty dodgy, so if there's a much better way to go about this, I'm all ears.

+1  A: 

If everything else fails, let the private method return Object and use casts in the public methods.

ammoQ
+8  A: 

I strongly suggest that reflection-mojo should be avoided here. A function must do one thing propertly, and must not depend on who call it.

A better way to do the same would be to refactor doSomething() into smaller functions, create two new functions called doSomethingFor1() and doSomethingFor2(). Both these functions can reuse the refactored parts of the old doSomething().

Now have doSomething1() call and use doSomethingFor1().

Likewise, doSomething2() should use doSomethingFor2().

cheers,

jrh.

Here Be Wolves
Yes, this is what I came up with as well. I agree that a method shouldn't have a dependancy on its caller. But what if doSomethingFor1() and doSomethingFor2() share the exact same code, the only thing differentiating them being which object they add as the key to the map? Wouldn't that be a big duplication of code?
The Dissonant
duplication must be countered by properly refactoring the functions and placing duplicate code in common functions/methods.
Here Be Wolves
@The Dissonant: so use a parameter to indicate which key to add. Don't infer it from who called you.
Laurence Gonsalves
A: 

You can definitely do it by analyzing the stack using reflection. But I would try to avoid it and:

  • return base type (or object) and cast it properly in the caller, which you will have to do anyways
  • pass a parameter to the private method indicating somehow what to do and return
van
+1  A: 

The first question is why you need this exact structure.

But the safe way is to make it return

public Map<Object, Type3> doSomething();

if there is no common supertype for Type1 and Type2. If you do have a common super type, you could use that instead of object.

It would be best to refactor the code that this isn't required.

Blair Zajac
I'm wondering, how would doSomething() know which subclass of Object (ie. Type1 or Type2) to add as the Map key? It needs to be able to perform a check.
The Dissonant
+1  A: 

I need a bit of info that is missing from your question, as for example how type1 and type 2 are instantiated, since you can't instantiate directly from arbitrary type parameters. Now, we assume there is some common data used to instantiate both type1 and type2. Otherwise, there is no good reason to use one method for both. In the latter case, this would mean one function doing two separate things based on type, which is bad programming. If you're going to have logic which is directly based on the calling method, simply have two private method and refactor out the common stuff.

If you do only the instantiation differently, based on some data, the easiest way to do this would be to declare a private inner interface:

private interface Instantiator<T> {

     T getNew(Data data);        

}

Now, assuming that you can instantiate your types, you can actually use this:

private <T> Map<T,Type3> doSomething(Instantiator<T> instantiator) {...}

as a method signature, and have the public methods call it with the appropriate Instantiator implementation .

+3  A: 

Please note: I've misunderstood the question, so this answer is opposite of what the question is as asking for. I will leave this up for reference as community wiki, but this does not answer the original question.

If there were a common subtype to Type1 and Type2 called SuperType, then saying that the first type is ? extends SuperType would work.

Here's a little example that uses Integer and Double as the two types, and their common ancestor as Number:

private Map<Integer, String> doSomethingForInteger() {
  HashMap<Integer, String> map = new HashMap<Integer, String>();
  map.put(10, "Hello");
  return map;
}

private Map<Double, String> doSomethingForDouble() {
  HashMap<Double, String> map = new HashMap<Double, String>();
  map.put(3.14, "Apple");
  return map;
}

public Map<? extends Number, String> doSomething(boolean b) {
  if (b)
    return doSomethingForInteger();
  else
    return doSomethingForDouble();
}

Here, the doSomething method will return two types of HashMaps depending on the boolean that is passed in. Either HashMap<Integer, String> or HashMap<Double, String> is returned by the doSomething method.

Actually calling up doSomething with a boolean can be accomplished like this:

Map<? extends Number, String> map1 = doSomething(true);
Map<? extends Number, String> map2 = doSomething(false);

map1 will end up with Hashmap<Integer, String>, while map2 will get Hashmap<Double, String>.

coobird
I appreciate the answer, but you have it the wrong way around. I don't want the supertype method to be the public one, and I want all the map instantation/manipulation stuff to be done only in a single private method (ie. no duplication of it).
The Dissonant
Oops, sorry about that, I got it mixed up...
coobird
A: 

You could model the idea of a Closure :

interface MappingClosure {
    public void map(Map, MapProvider);
}


class Caller implements MappingClosure {
     public void map(final Map MAP, final MapProvider CALLEE) {
         this.MAP = specialise(MAP);
         // CALLEE USED AS KEY
     }
}


class MapProvider {  
    public Map getMap(final MappingClosure CALLER) {
        return CALLER.map(mapInstance, this);
    }
}

Then modifying the data to suit the objects needs are differed to a method withing the object that requires it.

_ande_turner_