30

Today I've added a extra security check behind my login forms, to slow down brute force attacks. I've got multiple login forms and made a nice easy to call function that does all the checking and then returns the result.

public static ValidateLoginResult validateLogin(HttpServletRequest request, String email, String password) {

The problem is the result is not a single value, the result consists of:

boolean ok
String errorMessage
boolean displayCaptcha

For this I created a new class. This all works fine.

But I often have handy utility functions that return multiple values and start to find it a bit annoying to create a new class for the result every time.

Is there a better way of returning multiple values? Or am I just lazy? :)

Stanley De Boer
  • 4,921
  • 1
  • 23
  • 31
TinusSky
  • 1,657
  • 5
  • 24
  • 32

7 Answers7

16

No, this kind of structure doesn't exists nativily in Java, but you can look at JavaTuples library that may suit your need and provide a quite elegant solution. Using a Triplet<Boolean, String, Boolean>

gma
  • 2,563
  • 15
  • 14
  • 1
    But with a small class you can access members with a getter, or make values optional. – moeTi May 17 '13 at 12:45
  • 10
    Writing `return new Triplet(actual values)` isn't a shining example of elegance, either :) – Marko Topolnik May 17 '13 at 12:45
  • 1
    This is actually quite a topic of discussion: http://stackoverflow.com/questions/156275/what-is-the-equivalent-of-the-c-pairl-r-in-java – SubSevn May 17 '13 at 12:47
  • @Marko Topolnik : For sure but it's easily reusable in many situations and is as long as new MyCustomObject(value1, value2, value3) :-) – gma May 17 '13 at 12:50
  • @gma You have a blind spot for the three type arguments, it seems :) Such things do matter to me. – Marko Topolnik May 17 '13 at 12:56
  • I must admit i didn't know javatuples yet, they seem seem like a good idea, i'm going to given them a try. Thanks for the idea (and everybody else too)! – TinusSky May 17 '13 at 13:08
  • 7
    So if your method returning a Triplet object now needs to return an optional 4th bit of data, you have to change the return type from a Triple to a Quartet? No thanks. I'd rather just roll a ReturnObject that I could add another property to without breaking client code. – splungebob May 17 '13 at 13:27
  • Little thing i bumped on: http://projectlombok.org/ allows a lot smaller classes: @AllArgsConstructor public class ValidateLoginResult {@Getter private boolean ok;@Getter private String errorMessage;@Getter private boolean displayCaptcha;}. Works quite nicely! – TinusSky May 17 '13 at 15:32
5

Not sure about "best practice" but a pragmatic option is to return a Map<String, String>? E.g.

myMap.put("result", "success");
myMap.put("usernameConfirmed", "bigTom");

return myMap;

Probably flies in the face of a million OO principles but I hear you re wanting to avoid a proliferation of result classes.

You could alternatively use Map<String, Object> and be stricter with type checks on stored objects: Strings, Booleans, Dates, etc.

Brian
  • 6,391
  • 3
  • 33
  • 49
5

I can't really think of a better, cleaner and more object-oriented way of returning multiple values from a function than encapsulating them in a class.

Ideally, the multiple values you want to return are conceptually all part of the same class, so it makes sense to group them this way; if they don't, then you should probably decompose your function in some smaller functions that return each of the values you need outside of the function itself.

As far as I can tell, some IDEs also have facilities to help encapsulating multiple values in a class: for instance, Eclipse has Refactor --> Extract class...

Raibaz
  • 9,280
  • 10
  • 44
  • 65
  • 3
    There is nothing object-oriented about this; it's mostly about Java's static typing and weak generics. JavaScript is object-oriented and doesn't need another class for each tuple. – Marko Topolnik May 17 '13 at 12:47
  • 1
    it seems like every Java programmer wants to create an object for everything... what's wrong with good ol' data? – jassa Mar 11 '15 at 22:57
  • 1
    Does Java even give you an alternative out of the box? Maybe it's not the programmer's fault. – jassa Mar 11 '15 at 22:59
5

You can define a Pair<A, B> class, and a Triplet<A, B, C> class, and that would solve the problem of returning 2 and 3 values while ensuring type-safety. In this particular case, the signature could be

public static boolean validateLogin(HttpServletRequest request,
            String email, String password, Pair<Message, Boolean> outputIfOk);

Or even better, in a servlet context, it may make sense to set some well-documented request attributes.

If you find yourself needing special classes to return results very often, you can most likely refactor those clases to share a common ancestor (say, have a RequestStatus which includes the 'ok' and 'message' fields).

Other than that, yes, you are being lazy -- custom clases will always be more self-documenting than Pairs and Triplets.

tucuxi
  • 17,561
  • 2
  • 43
  • 74
1

You can return an Object[] array, java autoboxes so its more easy to use. If it's just for a short distance handover, why not. Ofc its risky, possible class cast trouble, nullchecks etc

but its easy to write and use.

then again, a static inner class is quickly created and if you put it right next to the method returning it, you also know where to find it (usually near the origin)

NikkyD
  • 2,209
  • 1
  • 16
  • 31
0

I'd probably just go the class route myself, but depending on what you want the function to return, you might be able to get away with returning some sort of container of values.

Bit-64
  • 1
  • 2
0

Here is a possible solution I picked up from another discussion, and enhanced a bit. It uses a public inner class with a private constructor:

public class Test {
    // Internal storage unit for the two values:
    // 'name' and 'age'.
    private Pair<String, Integer> info;

    public Test() {
        // Empty default constructor.
    }

    /**
     * The two values are stored in the Test class.
     *
     * @param name
     * @param age
     */
    public void setInfo(String name, int age) {
        info = new Pair<>(name, age);
    }

    /**
     * The 'name' and 'age' values are returned in a
     * single object.
     *
     * @return Both values in a Pair object.
     */
    public Pair<String, Integer> getInfo() {
        return info;
    }

    /**
     *  This is an Inner Class for providing pseudo 'tuplet'
     *  as a 'get' return value.
     *
     * @param <F> first internally stored value.
     * @param <S> second internally stored value.
     */
    public class Pair<F, S> {

        public final F first;
        public final S second;

        // This constructor is private to prevent
        // it being instantiated outside its
        // intended environment.
        private Pair(F first, S second) {
            this.first = first;
            this.second = second;
        }

        @Override
        public String toString(){
            return first + ", " + second;
        }
    }

    /**
     * main method for testing of the class only.
     *
     * @param args
     */
    public static void main(String args[]) {
        Test test = new Test();

        test.setInfo("Peter Smith", 35);
        Test.Pair<String, Integer> pair = test.getInfo();

        System.out.println("name: " + pair.first);
        System.out.println("age: " + pair.second);
        System.out.println(pair.toString());
    }
}