0

At the moment I have an ArrayDeque of a Tuple as follows (I thought having a class in this might help), which is currently serialized and sent over a network:

private ArrayDeque<Triple<Integer, Object, Class>> paramList = new ArrayDeque<>();

The object types in the Tuple can be primitive such as ints, bool or real classes such as Date, time (none of which are my implementations).

Right now I'm iterating through the list and calling the method depending on what class type it is, obviously this becomes tedious modify and update when. For example (st is PreparedStatement):

for (Triple<Integer, Object, Class> param : paramList) {
    if(param.getMiddle() instanceof String){
        st.setString(parm.getLeft(),param.getMiddle());
    }else if(param.getMiddle() instanceof Integer){
        st.setInt(parm.getLeft(),param.getMiddle());
    } //... more class type checks doing the same thing
}

I came across this https://stackoverflow.com/a/5579385/5858208 post but as it's not classes I've implemented I can't really do much.

So my question is, is there a more efficient and maintainable way of implementing this idea since I have exactly what class-type it is in the tuple object?

A.A
  • 743
  • 1
  • 8
  • 20
  • Please let me know why the downvotes and I'll update the question accordingly – A.A Dec 06 '17 at 18:00
  • What is the variable `st`? You could overload a method called set and just call it, letting the class do its work – Rômulo M. Farias Dec 06 '17 at 18:01
  • I didn't dv - but the Tuple - Triple and Dequeue - Deque aberrations are strange. – laune Dec 06 '17 at 18:02
  • st is PreparedStatement object that is implemented by PG JDBC drivers – A.A Dec 06 '17 at 18:02
  • @laune apologise I typically refer to Pairs, Triples, quad etc as Tuples https://en.wikipedia.org/wiki/Tuple – A.A Dec 06 '17 at 18:04
  • You can create a class which wraps the PreparedStatement object. You won't have less lines, but the code quality will be higher and better to read – Rômulo M. Farias Dec 06 '17 at 18:04
  • @RômuloM.Farias: That won't actually help at all; overload resolution is resolved at compile-time. – SLaks Dec 06 '17 at 18:10
  • Not sure why you cant use the pattern described in your linked stackoverflow answer? – tsolakp Dec 06 '17 at 18:14
  • @tsolakp All the objects linked are Java implementation classes (String, double, int, Date, Time etc) and doesn't sound like correct approach to extend all these class implementations to inherit an interface, and would be even more of a pain to maintain – A.A Dec 06 '17 at 18:16

1 Answers1

0

You could create a Map that contains as keys all the classes that your code supports and as values lambda expressions that call the correct setter.

Something along these lines:

@FunctionalInterface
interface PSConsumer<T,U> {
    void accept(T t, U u) throws SQLException;
}

private Map<Class<?>, PSConsumer<PreparedStatement, Triple<Integer, Object, Class<?>>>> setters = new HashMap<>();
{
    setters.put(String.class, (st, param) -> st.setString(param.getLeft(), (String) param.getMiddle()));
    setters.put(Integer.class, (st, param) -> st.setInt(param.getLeft(), (Integer) param.getMiddle()));
    // add here code for all the classes that you support
}

for (Triple<Integer, Object, Class<?>> param : paramList) {
    // this will throw an NPE if param.getMiddle() returns null or it's class is not in the map:
    setters.get(param.getMiddle().getClass()).accept(st, param);
}

To extract values in a similar way you could use definitions likes these:

@FunctionalInterface
interface RSExtractor<T,U,R> {
    R apply(T t, U u) throws SQLException;
}
private Map<Class<?>, RSExtractor<ResultSet, Triple<Integer, Object, Class<?>>, Object>> getters = new HashMap<>();
{
    getters.put(String.class, (rs, param) -> rs.getString(param.getLeft()));
    getters.put(Integer.class, (rs, param) -> rs.getInt(param.getLeft()));
}

public List<Object> get(ResultSet rs) throws SQLException {
    List<Object> results = new ArrayList<>();
    for (Triple<Integer, Object, Class<?>> param : paramList) {
        results.add(getters.get(param.getRight()).apply(rs, param));
    }
    return results;
}

Please note that the interface RSExtractor looks much like the interface java.util.function.BiFunction, except that this interface allows the implementing method to throw an SQLException

Thomas Kläger
  • 17,754
  • 3
  • 23
  • 34
  • I'm pretty new to Java streams, would this implementation be more or less efficient in terms of performance. Although it's a lot nicer to read/maintain. I'll give it a go and let you know how it goes – A.A Dec 06 '17 at 20:21
  • I have a small question, how exactly does lamda function work in this Hashmap. from what I understand you put a class as key and PSConsumer as the object, does the lamda function act implemented accept function, if so how does Java know this, what if I added another method in the interface? – A.A Dec 07 '17 at 20:29
  • @A.A a functional interface (one that can be implemented with a lambda function) must only have one abstract method (https://stackoverflow.com/questions/17913409, https://ocpjava.wordpress.com/2016/04/05/java-8-functional-interfaces-sam/). Because of this the java compiler knows how to make the connection from the method name in the interface to the lambda expression. If you add a second (abstract) method to the interface it is no longer a functional interface and you could no longer implement it with a lambda expression. – Thomas Kläger Dec 07 '17 at 21:32
  • Thanks for the anwser, I was wondering if it's possible to do the same thing with get methods? If so how would I return value from lamada? – A.A Dec 12 '17 at 18:24
  • @A.A it's possible with get methods too. How to return the value from the lambda depends on how you want to store it: do you want to replace all `middle` values or do you want do add all values to a `List`? – Thomas Kläger Dec 13 '17 at 05:21
  • Is it possible to get a single value similar to a normal method, eg I call similar .accept() method which in turn calls rs.getString(param.GetMiddle()) (rs in this case is a resultset) and returns the value from getString implementation. – A.A Dec 13 '17 at 14:30