日常开发中,我们经常需要在一个方法里返回多个值,在go中,这个很好实现:

func getResult() (string, int) {
    return "result", 30
}

在Java中,可考虑以下实现:

1、自定义对象

首先能想到的应该就是封装成一个对象,这里以两个基本数据类型的返回值为例,两个引用数据类型也是一样:

// 自定义类
public class UserInfo {
    private final String name;
    private final int age;

    public UserInfo(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getters
    public String getName() { return name; }
    public int getAge() { return age; }
}

// 方法直接返回这个自定义类
public UserInfo getUserInfo() {
    return new UserInfo("Alice", 30);
}

// 调用方
UserInfo userInfo = getUserInfo();
String name = userInfo.getName();
int age = userInfo.getAge();

2、JDK14+的record关键字

如果项目中使用的JDK是14+版本,那可以使用record关键字,简化DTO的定义

// 定义 Record(不可变类)
public record UserInfo(String name, int age) {}

// 方法返回 Record
public UserInfo getUserInfo() {
    return new UserInfo("Alice", 30);
}

调用方获取值,直接访问字段:

// 调用方
UserInfo userInfo = getUserInfo();
String name = userInfo.name(); // 直接访问字段
int age = userInfo.age();

优点:

  • 代码简洁(自动生成 equals()/hashCode()/toString())
  • 不可变性(线程安全)

3、第三方框架

很多三方框架中,有类似的现成类提供,比如Apache RocketMQ源码中的Pair类,源码:

package org.apache.rocketmq.common;

public class Pair<T1, T2> {
    private T1 object1;
    private T2 object2;

    public Pair(T1 object1, T2 object2) {
        this.object1 = object1;
        this.object2 = object2;
    }

    public T1 getObject1() {
        return object1;
    }

    public void setObject1(T1 object1) {
        this.object1 = object1;
    }

    public T2 getObject2() {
        return object2;
    }

    public void setObject2(T2 object2) {
        this.object2 = object2;
    }
}

以上封装比较简单,Apache Commons工具包中,还封装了更优的Pair和Triple类,分别用于两个值、三个值的情况,Pair翻译即配对,Triple即三重的

在这里插入图片描述

使用示例:

// 使用 Apache Commons Lang3 的 Pair
import org.apache.commons.lang3.tuple.Pair;

public Pair<String, Integer> getUserInfo() {
    String name = "Alice";
    int age = 30;
    return Pair.of(name, age);
}

// 调用方
Pair<String, Integer> userInfo = getUserInfo();
String name = userInfo.getLeft();
int age = userInfo.getRight();

4、使用List

如果返回值类型一致,使用List倒也是一种实现方式,但要考虑下标越界异常的case

public List<String> getUserNameAndEmail() {
    String name = "Alice";
    String email = "alice@example.com";
    return Arrays.asList(name, email); // 或者 List.of(name, email)(Java 9+)
}

// 调用方
List<String> userInfo = getUserNameAndEmail();
String name = userInfo.get(0); // 直接获取,无需转型
String email = userInfo.get(1);

不过这样可读性比较差,从0、1这样的下标,无法直观看到字段语义,临时用一下也行

5、使用Map

使用键值对存,但这样又得维护键的名字,也不是什么最优解:

public Map<String, Object> getUserInfo() {
    Map<String, Object> map = new HashMap<>();
    map.put("name", "Alice");
    map.put("age", 30);
    return map;
}

// 调用方
Map<String, Object> userInfo = getUserInfo();
String name = (String) userInfo.get("name"); // 需转型
int age = (int) userInfo.get("age");

6、传一个空壳,由被调方传值

以下只是体现一种思路,即:我拿一个空碗给你,你盛好饭,我开吃

public void getUserInfo(UserInfoHolder holder) {
    holder.setName("Alice");
    holder.setAge(30);
}

// 调用方
UserInfoHolder holder = new UserInfoHolder();
getUserInfo(holder);
String name = holder.getName();
int age = holder.getAge();

总结下:

方案 适用场景 优点 缺点
自定义类 业务逻辑复用 语义清晰,易扩展 需额外定义类
Record JDK 14+ 的不可变数据 代码简洁,线程安全 需高版本 JDK
Pair/Triple 临时简单数据 无需定义新类 可读性差
List/数组 快速原型开发 代码简短 类型不安全
Map 动态键值场景 灵活性高 键名易错,类型不安全
参数修改 需修改外部状态(不推荐) 类似 C/C++ 风格 破坏封装性

非临时使用,建议优先考虑封装自定义类或者Record

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐