在 Java 中返回多个值的最佳实践?

Posted

技术标签:

【中文标题】在 Java 中返回多个值的最佳实践?【英文标题】:Best practice for returning multiple values in Java? 【发布时间】:2013-05-12 15:14:40 【问题描述】:

今天,我在登录表单后面添加了额外的安全检查,以减缓暴力攻击。我有多个登录表单,并制作了一个易于调用的函数,该函数执行所有检查,然后返回结果。

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

问题是结果不是单个值,结果包括:

boolean ok
String errorMessage
boolean displayCaptcha

为此,我创建了一个新类。这一切都很好。

但我经常有方便的实用函数返回多个值,并且开始发现每次为结果创建一个新类有点烦人。

有没有更好的方法来返回多个值?还是我只是懒惰? :)

【问题讨论】:

没有,但你并不懒惰 :) 这对我来说也很麻烦。 不要新建类,改用generic classes。 新类记录语义,编译时检查返回值。为了可维护性,值得花时间。 懒惰可以提高效率:) 三年后才有同样的感受。我落后了多少:( 【参考方案1】:

不,这种结构在 Java 中不存在,但您可以查看JavaTuples library,它可能适合您的需求并提供非常优雅的解决方案。使用Triplet<Boolean, String, Boolean>

【讨论】:

但是通过一个小类,您可以使用 getter 访问成员,或者使值可选。 return new Triplet<Boolean, String, Boolean>(actual values) 也不是优雅的光辉典范:) 这其实是个相当热门的话题:***.com/questions/156275/… @Marko Topolnik :当然,但它在许多情况下很容易重复使用,并且只要 new MyCustomObject(value1, value2, value3) :-) 所以如果你的返回 Triplet 对象的方法现在需要返回可选的第 4 位数据,你必须将返回类型从 Triplet 更改为 Quartet?不用了,谢谢。我宁愿只滚动一个 ReturnObject,我可以在不破坏客户端代码的情况下添加另一个属性。【参考方案2】:

不确定“最佳实践”,但务实的选择是返回Map<String, String>?例如

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

return myMap;

可能会违反一百万个 OO 原则,但我听说您希望避免结果类的泛滥。

您也可以使用 Map<String, Object> 并对存储对象进行更严格的类型检查:字符串、布尔值、日期等。

【讨论】:

如果使用枚举作为键值,您可以避免拼写错误和自文档。【参考方案3】:

您可以定义一个Pair<A, B> 类和一个Triplet<A, B, C> 类,这将解决返回2 和3 值的问题,同时确保类型安全。在这种特殊情况下,签名可以是

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

或者更好的是,在 servlet 上下文中,设置一些有据可查的请求属性可能是有意义的。

如果您发现自己经常需要特殊的类来返回结果,您很可能可以重构这些类以共享一个共同的祖先(例如,拥有一个包含“ok”和“message”字段的 RequestStatus)。

除此之外,是的,您很懒惰——自定义类总是比 Pairs 和 Triplets 更具自我记录性。

【讨论】:

好分析器,org.apache.commons.lang3.tuple 的包中还有一个 ImmutablePair【参考方案4】:

我真的想不出比将它们封装在一个类中更好、更简洁、更面向对象的方法来从函数返回多个值。

理想情况下,您要返回的多个值在概念上都是同一个类的一部分,因此以这种方式对它们进行分组是有意义的;如果他们不这样做,那么您可能应该将您的函数分解为一些较小的函数,这些函数在函数本身之外返回您需要的每个值。

据我所知,一些 IDE 还具有帮助在一个类中封装多个值的功能:例如,Eclipse 有 Refactor --&gt; Extract class...

【讨论】:

这与面向对象无关;它主要是关于 Java 的静态类型和弱泛型。 javascript 是面向对象的,不需要为每个元组使用另一个类。 似乎每个 Java 程序员都想为所有东西创建一个对象......好的 ol' 数据有什么问题? Java 是否甚至为您提供了开箱即用的替代方案?也许这不是程序员的错。【参考方案5】:

您可以返回一个 Object[] 数组,java 自动装箱,因此它更易于使用。如果只是为了短距离切换,为什么不呢。 Ofc 有风险,可能的类转换问题,nullchecks 等

但它易于编写和使用。

再一次,一个静态内部类被快速创建,如果你把它放在返回它的方法旁边,你也知道在哪里找到它(通常在原点附近)

【讨论】:

【参考方案6】:

我可能会自己走类路线,但根据您希望函数返回的内容,您可能能够通过返回某种值的容器而侥幸。

【讨论】:

【参考方案7】:

这是我从另一个讨论中获得的一个可能的解决方案,并进行了一些改进。 它使用带有私有构造函数的公共内部类:

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());
    

【讨论】:

以上是关于在 Java 中返回多个值的最佳实践?的主要内容,如果未能解决你的问题,请参考以下文章

关于引用参数或显式返回值的 PHP 方法的最佳实践

从可能不存在的 JToken 中获取价值(最佳实践)

DAO 级别布尔方法的最佳实践

【翻译】Prometheus最佳实践 Summary和Histogram

通过本地 wifi(UNITY / Mirror)提供 50 多个 blendshape 的最佳实践

在 REST 调用中使用 URI 作为参数值的最佳实践