Перейти к основному содержимому

Как вернуть несколько значений из метода Java

· 6 мин. чтения

1. Обзор

В этом руководстве мы изучим различные способы возврата нескольких значений из метода Java.

Во-первых, мы вернем массивы и коллекции. Затем мы покажем, как использовать классы-контейнеры для сложных данных, и узнаем, как создавать универсальные классы кортежей.

Наконец, мы увидим примеры использования сторонних библиотек для возврата нескольких значений.

2. Использование массивов

Массивы могут использоваться для возврата как примитивных, так и ссылочных типов данных .

Например, следующий метод getCoordinates возвращает массив из двух значений типа double :

double[] getCoordinatesDoubleArray() {

double[] coordinates = new double[2];

coordinates[0] = 10;
coordinates[1] = 12.5;

return coordinates;
}

Если мы хотим вернуть массив различных ссылочных типов, мы можем использовать общий родительский тип в качестве типа массива :

Number[] getCoordinatesNumberArray() {

Number[] coordinates = new Number[2];

coordinates[0] = 10; // Integer
coordinates[1] = 12.5; // Double

return coordinates;
}

Здесь мы определили массив координат типа Number , потому что это общий класс для элементов Integer и Double .

3. Использование коллекций

С универсальными коллекциями Java мы можем возвращать несколько значений общего типа .

Фреймворк коллекций имеет широкий спектр классов и интерфейсов. Однако в этом разделе мы ограничим обсуждение интерфейсами List и Map .

3.1. Возврат значений аналогичного типа в списке

Для начала давайте перепишем предыдущий пример массива, используя List<Number> :

List<Number> getCoordinatesList() {

List<Number> coordinates = new ArrayList<>();

coordinates.add(10); // Integer
coordinates.add(12.5); // Double

return coordinates;
}

Как и Number[] , коллекция List<Number> содержит последовательность элементов смешанного типа одного и того же общего типа.

3.2. Возврат именованных значений в карте

Если мы хотим назвать каждую запись в нашей коллекции, вместо этого можно использовать карту :

Map<String, Number> getCoordinatesMap() {

Map<String, Number> coordinates = new HashMap<>();

coordinates.put("longitude", 10);
coordinates.put("latitude", 12.5);

return coordinates;
}

Пользователи метода getCoordinatesMap могут использовать ключи « долгота» или « широта» с методом Map#get для получения соответствующего значения.

4. Использование классов контейнеров

В отличие от массивов и коллекций, классы-контейнеры (POJO) могут содержать несколько полей с разными типами данных .

Например, следующий класс Coordinates имеет два разных типа данных, double и String :

public class Coordinates {

private double longitude;
private double latitude;
private String placeName;

public Coordinates(double longitude, double latitude, String placeName) {

this.longitude = longitude;
this.latitude = latitude;
this.placeName = placeName;
}

// getters and setters
}

Использование классов-контейнеров, таких как Coordinates , позволяет нам моделировать сложные типы данных со значимыми именами .

Следующим шагом является создание и возврат экземпляра Coordinates :

Coordinates getCoordinates() {

double longitude = 10;
double latitude = 12.5;
String placeName = "home";

return new Coordinates(longitude, latitude, placeName);
}

Следует отметить, что рекомендуется делать классы данных, такие как Coordinates , неизменяемыми . Таким образом мы создаем простые, потокобезопасные, совместно используемые объекты.

5. Использование кортежей

Подобно контейнерам, кортежи хранят поля разных типов. Однако они отличаются тем, что не зависят от приложения .

Они специализированы, когда мы используем их для описания того, какие типы мы хотим, чтобы они обрабатывали, но являются контейнерами общего назначения для определенного количества значений. Это означает, что нам не нужно писать собственный код, чтобы иметь их, и мы можем использовать библиотеку или создать общую единую реализацию.

Кортеж может состоять из любого количества полей и часто называется кортежем n, где n — количество полей. Например, Tuple2 — это кортеж с двумя полями, Tuple3 — это кортеж с тремя полями и так далее.

Чтобы продемонстрировать важность кортежей, рассмотрим следующий пример. Предположим, что мы хотим найти расстояние между точкой Coordinates и всеми остальными точками внутри List<Coordinates> . Затем нам нужно вернуть этот самый удаленный объект Coordinate вместе с расстоянием.

Давайте сначала создадим общий кортеж из двух полей:

public class Tuple2<K, V> {

private K first;
private V second;

public Tuple2(K first, V second){
this.first = first;
this.second = second;
}

// getters and setters
}

Далее давайте реализуем нашу логику и используем экземпляр Tuple2<Coordinates, Double> для переноса результатов:

Tuple2<Coordinates, Double> getMostDistantPoint(List<Coordinates> coordinatesList, 
Coordinates target) {

return coordinatesList.stream()
.map(coor -> new Tuple2<>(coor, coor.calculateDistance(target)))
.max((d1, d2) -> Double.compare(d1.getSecond(), d2.getSecond())) // compare distances
.get();
}

Использование Tuple2<Coordinates, Double> в предыдущем примере избавило нас от создания отдельного класса-контейнера для одноразового использования именно с этим методом .

Как и контейнеры, кортежи должны быть неизменяемыми . Кроме того, из-за их универсального характера нам следует использовать кортежи внутри, а не как часть нашего общедоступного API .

6. Сторонние библиотеки

Некоторые сторонние библиотеки реализовали неизменяемый тип Pair или Triple . Apache Commons Lang и javatuples являются яркими примерами. Когда у нас есть эти библиотеки в качестве зависимостей в нашем приложении, мы можем напрямую использовать типы Pair или Triple , предоставляемые библиотеками, вместо того, чтобы создавать их самостоятельно.

Давайте рассмотрим пример использования Apache Commons Lang для возврата объекта Pair или Triple .

Прежде чем двигаться дальше, давайте добавим зависимость commons-lang3 в наш pom.xml:

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>

6.1. ImmutablePair от Apache Commons Lang

Тип ImmutablePair из Apache Commons Lang — это именно то, что нам нужно: неизменяемый тип, использование которого просто.

Он содержит два поля: левое и правое . Давайте посмотрим, как заставить наш метод getMostDistantPoint возвращать объект типа ImmutablePair :

ImmutablePair<Coordinates, Double> getMostDistantPoint(
List<Coordinates> coordinatesList, Coordinates target) {
return coordinatesList.stream()
.map(coordinates -> ImmutablePair.of(coordinates, coordinates.calculateDistance(target)))
.max(Comparator.comparingDouble(Pair::getRight))
.get();
}

6.2. ImmutableTriple от Apache Commons Lang

ImmutableTriple очень похож на ImmutablePair . Единственная разница в том, что, как следует из названия, ImmutableTriple содержит три поля: левое , среднее и правое. ``

Теперь давайте добавим новый метод к нашему вычислению координат, чтобы показать, как использовать тип ImmutableTriple .

Мы пройдемся по всем точкам в List<Coordinates> , чтобы узнать минимальное , среднее и максимальное расстояния до заданной целевой точки.

Давайте посмотрим, как мы можем вернуть три значения одним методом, используя класс ImmutableTriple :

ImmutableTriple<Double, Double, Double> getMinAvgMaxTriple(
List<Coordinates> coordinatesList, Coordinates target) {
List<Double> distanceList = coordinatesList.stream()
.map(coordinates -> coordinates.calculateDistance(target))
.collect(Collectors.toList());
Double minDistance = distanceList.stream().mapToDouble(Double::doubleValue).min().getAsDouble();
Double avgDistance = distanceList.stream().mapToDouble(Double::doubleValue).average().orElse(0.0D);
Double maxDistance = distanceList.stream().mapToDouble(Double::doubleValue).max().getAsDouble();

return ImmutableTriple.of(minDistance, avgDistance, maxDistance);
}

7. Заключение

В этой статье мы узнали, как использовать массивы, коллекции, контейнеры и кортежи для возврата нескольких значений из метода. Мы можем использовать массивы и коллекции в простых случаях, поскольку они обертывают один тип данных.

С другой стороны, контейнеры и кортежи полезны при создании сложных типов, причем контейнеры обеспечивают лучшую читабельность.

Мы также узнали, что в некоторых сторонних библиотеках реализованы парные и тройные типы, и увидели несколько примеров из библиотеки Apache Commons Lang.

Как обычно, исходный код этой статьи доступен на GitHub .