Подтвердить что ты не робот

Самый короткий способ перебора без списка?

Предположим, у меня есть 3 экземпляра сканера, которые я хочу закрыть.

Я мог бы сделать

sc.close()

для каждого из сканеров.

Или я мог бы сделать что-то вроде

for (Scanner sc: new Scanner[]{sc1,sc2,sc3}) {
    sc.close();
}

Есть ли более короткий способ сделать это с помощью Java 8?

Что-то похожее на?

{sc1,sc2,sc3}.forEach((sc) -> sc.close());
4b9b3361

Ответ 1

Начиная с Java 7 вы должны использовать try-with-resources

try(Scanner sc1 = new Scanner(""); 
    Scanner sc2 = new Scanner(""); 
    Scanner sc3 = new Scanner("")){

}
//all scanners get closed implicitly

Таким образом, вам совсем не нужен код.

Проблема со всеми конструкциями for-each или stream заключается в том, что - теоретически - если первый close() завершился с ошибкой при вызове метода исходного метода close(), следующие сканеры не будут закрыты. Реализация Scanner.close() допускает любое исключение IOException, но не может быть другого исключения.

Конструкция try-with-resources имеет дело с этим, петли этого не делают.


EDIT. Хотя ваш вопрос был нацелен на более общий подход, вышеупомянутое решение было ответом на вашу конкретную проблему: для работы с ресурсами AutoCloseable, которые в любом случае должны использоваться с try-with-resources, не требуя специальной обработки метода close (= кратчайшее решение вашей конкретной проблемы).

Что касается более общего вопроса о работе с произвольными элементами (которые не являются ресурсами), у Java есть как минимум два варианта:

Создание списка из массива /Varargs и его повторение

for(YourItemType item : Arrays.asList(your,items,here)) {
  //do something
}

Создание потока из массива /Varargs и применение к нему функций

Stream.of(your,items,here).forEach(item -> { doSomething});

Конечно, "doSomething" можно заменить ссылкой на метод

Stream.of(your,items,here).forEach(this::myMethod);
...
void myMethod(YourItemType item){
  //doSomething
} 

Проблема с этим подходом заключается в том, что проверенное исключение должно рассматриваться явно в лямбда-выражениях. Давайте рассмотрим приведенный выше пример и пусть myMethod вытащите исключенное исключение

void myMethod(YourItemType item) throws Exception

в этом случае ваш оператор потока должен выглядеть как

Stream.of(your,items,here).forEach(item -> {
  try {
    myMethod(item);
  } catch (Exception e){
    //omit or throw new RuntimeException(e);
  };

Это не выглядит так хорошо. Но мы могли бы поместить тело лямбда в отдельный метод.

void myMethodQuietly(YourItemType item) {
  try {
    myMethod(item);
  }catch(Exception e){
    //omit or throw new RuntimeException(e);
  }
}

Stream.of(your,items,here).forEach(this::myMethodQuietly);

Этот подход может представлять интерес для вашей конкретной проблемы с ресурсами. Мы можем поместить все это в CompositeAutoCloseable, который берет ресурсы, созданные вне класса, которые должны быть безопасно закрыты при вызове close()

public class CompositeAutoCloseable implements AutoCloseable {

  private List<Closeable> resources;

  public CompositeAutoCloseable(Closeable... resources) {
    this.resources = Arrays.asList(resources);
    //you could use a stream here too
  }

  @Override
  public void close() {
      this.resources.stream().forEach(this::closeQuietly);
  }

  void closeQuietly(Closeable res) {
    if(res == null)  {
        return;
    }
    try {
        res.close();
    }catch(Exception e){
        //omit
    }
  } 
}

И как только у вас будет такой вспомогательный класс, вы можете использовать его снова с помощью try-with-resources.

try(CompositeAutoCloseable cac = new CompositeAutoCloseable(sc1,sc2,sc3)) {
  //do something
}

Я оставляю это для вас, чтобы решить, имеет ли это смысл по сравнению с внутренним решением;)

Ответ 2

Если экземпляр отделяется от удаления, используйте Guava com.google.common.io.Closer.

Ответ 3

Хотя иногда существует корреляция между длиной кода и его качеством, что не является хорошим критерием для использования кода.

Я бы, вероятно, использовал varargs и сделал бы что-то вроде:

private void closeScanner(Scanner... scanners) {
    // Faff around with Java 8 in here if you like.
    for (Scanner s : scanners) {
        s.close();
    }
}

    // Some code.
    closeScanner(s, t, u);