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; }
Здесь мы определили координаты массив типа Число , потому что это общий класс между Целочисленными и двойными элементами.
3. Использование коллекций
С помощью общих коллекций Java , мы можем возвращать несколько значений общего типа .
Платформа коллекций имеет широкий спектр классов и интерфейсов. Однако в этом разделе мы ограничимся обсуждением интерфейсов List и Map .
3.1. Возврат значений аналогичного типа в списке
Для начала давайте перепишем предыдущий пример массива с помощью List :
ListgetCoordinatesList() { List coordinates = new ArrayList<>(); coordinates.add(10); // Integer coordinates.add(12.5); // Double return coordinates; }
Как и Number[] , коллекция List содержит последовательность элементов смешанного типа одного и того же общего типа.
3.2. Возврат именованных значений на карте
Если мы хотим назвать каждую запись в нашей коллекции, вместо этого можно использовать Map :
MapgetCoordinatesMap() { Map coordinates = new HashMap<>(); coordinates.put("longitude", 10); coordinates.put("latitude", 12.5); return coordinates; }
Пользователи метода get Coordinates Map могут использовать ключи ” долгота” или ” широта” с помощью метода Map#get для получения соответствующего значения.
4. Использование Классов Контейнеров
В отличие от массивов и коллекций, контейнерные классы (POJOS) могут обертывать несколько полей с различными типами данных .
Например, следующий класс Координаты имеет два разных типа данных: 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 getCoordinates() { double longitude = 10; double latitude = 12.5; String placeName = "home"; return new Coordinates(longitude, latitude, placeName); }
Следует отметить, что рекомендуется создавать классы данных, такие как Координаты |/неизменяемые . Таким образом, мы создаем простые, потокобезопасные, общие объекты.
5. Использование Кортежей
Как и контейнеры, кортежи хранят поля разных типов. Однако они отличаются тем, что не зависят от конкретного приложения .
Они специализированы, когда мы используем их для описания типов, которые мы хотим, чтобы они обрабатывались, но являются контейнером общего назначения с определенным количеством значений. Это означает, что нам не нужно писать пользовательский код, чтобы иметь их, и мы можем использовать библиотеку или создать общую единственную реализацию.
Кортеж может содержать любое количество полей и часто называется Кортеж n, где n – количество полей. Например, Tuple2-это кортеж из двух полей, Tuple3-кортеж из трех полей и так далее.
Чтобы продемонстрировать важность кортежей, рассмотрим следующий пример. Предположим, что мы хотим найти расстояние между точкой Координат и всеми другими точками внутри списка <Координаты> . Затем нам нужно вернуть этот самый удаленный объект координат вместе с расстоянием.
Давайте сначала создадим общий кортеж из двух полей:
public class Tuple2{ private K first; private V second; public Tuple2(K first, V second){ this.first = first; this.second = second; } // getters and setters }
Далее, давайте реализуем нашу логику и используем экземпляр Tuple2<Координаты, Двойной> , чтобы обернуть результаты:
Tuple2getMostDistantPoint(List 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 Double> в предыдущем примере избавило нас от создания отдельного контейнерного класса для одноразового использования с этим конкретным методом . Double>
Как и контейнеры, кортежи должны быть неизменяемыми . Кроме того, из-за их универсального характера мы должны использовать кортежи внутри, а не как часть нашего публичного API .
6. Сторонние библиотеки
Некоторые сторонние библиотеки реализовали неизменяемый тип Pair или Triple . Apache Commons Lang и javatuples являются примерами. Как только у нас есть эти библиотеки в качестве зависимостей в нашем приложении, мы можем напрямую использовать типы Pair или Triple , предоставляемые библиотеками, вместо того, чтобы создавать их самостоятельно.
Давайте рассмотрим пример использования Apache Commons Lang для возврата объекта Pair или Triple .
Прежде чем мы сделаем следующий шаг, давайте добавим зависимость commons-lang3 в ваш pom.xml:
org.apache.commons commons-lang3 3.11
6.1. Неизменяемая пара из Apache Commons Lang
Тип ImmutablePair из Apache Commons Lang-это именно то, что нам нужно: неизменяемый тип, использование которого является простым.
Он содержит два поля: left и right . Давайте посмотрим, как сделать так, чтобы наш метод get Most Distant Point возвращал объект типа ImmutablePair :
ImmutablePairgetMostDistantPoint( List coordinatesList, Coordinates target) { return coordinatesList.stream() .map(coordinates -> ImmutablePair.of(coordinates, coordinates.calculateDistance(target))) .max(Comparator.comparingDouble(Pair::getRight)) .get(); }
6.2. Неизменяемый пример из Apache Commons Lang
ImmutableTriple очень похож на ImmutablePair . Единственное различие заключается в том, что, как следует из названия, ImmutableTriple содержит три поля: left , middle, и right.
Теперь давайте добавим новый метод к нашему вычислению координат, чтобы показать, как использовать тип ImmutableTriple .
Мы собираемся пройти через все точки в List<Координаты> , чтобы узнать min , avg, и max расстояния до заданной целевой точки.
Давайте посмотрим, как мы можем вернуть три значения с помощью одного метода, используя класс ImmutableTriple :
ImmutableTriplegetMinAvgMaxTriple( List coordinatesList, Coordinates target) { List 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 .