Утверждения Java не отправляются на консоль при вызове из исполняемого

В моей программе я сделал утверждение - которое оценивается как ложное - из исполняемого, но никогда не видел вывода консоли об утверждении. Я хочу знать, ложны ли мои утверждения, но кажется, что runnable перехватывает все брошенные утверждения?

Ниже приведен простейший пример программы, которую я мог бы написать для демонстрации. (Утверждения включены. В противном случае программа вела бы себя иначе и печатала бы обе строки, а не только одну). Вывод программы.

Собирается утверждать ложь

Вот и все. После этого оператор assert бросает и что-то ловит, и я никогда не узнаю об этом. Я хочу знать об этом, что я делаю не так?

import java.nio.ByteBuffer;
import java.util.concurrent.*;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;

import java.awt.FlowLayout;
import javax.swing.*;

class App
{
  private static final ScheduledExecutorService sExecutor =
    Executors.newSingleThreadScheduledExecutor();

  // Main
  public static void main(String[] args)
  {
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() { createAndShowGUI(); } });

  }

  // Swing GUI
  private static void createAndShowGUI()
  {
    // Just create a swing thing. Boring
    JFrame frame = new JFrame("Title String");
    JLabel label = new JLabel("Hello World");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(label);
    frame.getContentPane().setLayout(new FlowLayout());
    frame.pack();
    frame.setVisible(true);

    // ********************************************
    // INTERESTING CODE HERE. We schedule a runnable which assert's false
    // but we never see a console assert error!
    // ********************************************
    sExecutor.schedule(new Runnable()
      { @Override public void run() { doAssertFalse(); }}, 0, TimeUnit.SECONDS);

  }

  public static void doAssertFalse()
  {
    System.out.println("About to assert False");
    assert false;
    System.out.println("Done asserting False");
  }
}

person Dijkstra    schedule 15.01.2013    source источник
comment
Включили ли вы утверждение в командной строке при запуске приложения?   -  person Laf    schedule 15.01.2013
comment
Утверждения включены. В противном случае программа вела бы себя иначе (она печатала бы обе строки, а не только одну).   -  person Dijkstra    schedule 15.01.2013
comment
Возможно, это связано с UncaughtExceptionHandler, связанным с вашим потоком. Вы пробовали установить собственный, чтобы увидеть, можно ли увидеть, как проходит исключение утверждения?   -  person Laf    schedule 15.01.2013
comment
@Laf: Как это сделать с исполнителем? Исполнитель создает для вас Thread, не предоставляя вам к ним доступа (afaik), и вам нужно поставить UncaughtExceptionHandler на экземпляр Thread.   -  person Daniel Kaplan    schedule 16.01.2013
comment
@DanielKaplan Я на самом деле не тестировал его, поэтому могу быть совершенно неправ, но Thread - это Runnable, поэтому вы можете создать свою собственную реализацию Thread со своим кодом и связать пользовательский UncaughtExceptionHandler с этим потоком, а затем использовать его как параметр при вызове исполнителя. Не знаю, будет ли это иметь значение. В любом случае, вы дали ответ, который, вероятно, более эффективен, чем мое предложение.   -  person Laf    schedule 16.01.2013


Ответы (2)


Это сделает это:

private static final ScheduledExecutorService sExecutor =
        Executors.newSingleThreadScheduledExecutor();

// Main
public static void main(String[] args)
{
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            try {
                createAndShowGUI();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } });

}

// Swing GUI
private static void createAndShowGUI() throws ExecutionException, InterruptedException {
    // Just create a swing thing. Boring
    JFrame frame = new JFrame("Title String");
    JLabel label = new JLabel("Hello World");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(label);
    frame.getContentPane().setLayout(new FlowLayout());
    frame.pack();
    frame.setVisible(true);

    // ********************************************
    // INTERESTING CODE HERE. We schedule a runnable which assert's false
    // but we never see a console assert error!
    // ********************************************
    ScheduledFuture<?> future = sExecutor.schedule(new Runnable() {
        @Override
        public void run() {
            doAssertFalse();
        }
    }, 0, TimeUnit.SECONDS);
    future.get();

}

public static void doAssertFalse()
{
    System.out.println("About to assert False");
    assert false;
    System.out.println("Done asserting False");
}

Обратите внимание, что я сохраняю результат schedule в переменную ScheduledFuture. Исключение не возвращается, пока вы не вызовете метод get() в будущем. Все исключения помещаются в ExecutionException.

К сожалению, это блокирует, поэтому вы можете получить исключение по-другому:

// Swing GUI
private static void createAndShowGUI() {
    // Just create a swing thing. Boring
    JFrame frame = new JFrame("Title String");
    JLabel label = new JLabel("Hello World");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.getContentPane().add(label);
    frame.getContentPane().setLayout(new FlowLayout());
    frame.pack();
    frame.setVisible(true);

    // ********************************************
    // INTERESTING CODE HERE. We schedule a runnable which assert's false
    // but we never see a console assert error!
    // ********************************************
    sExecutor.schedule(new Runnable() {
        @Override
        public void run() {
            try {
                doAssertFalse();
            } catch (Error e) {
                e.printStackTrace();
            }
        }
    }, 0, TimeUnit.SECONDS);

}

public static void doAssertFalse() {
    System.out.println("About to assert False");
    assert false;
    System.out.println("Done asserting False");
}

Обратите внимание, что я обнаружил ошибку, а не исключение. Я делаю это потому, что утверждения вызывают ошибку java.lang.AssertionError, а не исключение *.

У меня проблемы с поиском какой-либо документации в Javadoc, в которой говорится, что ScheduledExecutorService принимает исключения, если вы не выполняете эти действия, но с помощью моих тестов это, похоже, так.

person Daniel Kaplan    schedule 15.01.2013
comment
Это правда, исключение, созданное утверждением, будет перехвачено и напечатано. Но у меня создалось впечатление, что вы можете добавлять утверждения, чтобы программист мог знать, что что-то пошло не так. Должен ли я добавлять блок try catch вокруг каждого написанного мной утверждения (или метода утверждения)? - person Dijkstra; 15.01.2013
comment
Технически это вызвано не исключением, а ошибкой. Вот почему у вас проблемы. - person Daniel Kaplan; 15.01.2013
comment
Мой мыслительный процесс такой: Хм, я хочу убедиться, что numApples никогда не бывает отрицательным, поэтому я добавлю утверждение! assert numApples ›= 0. Но иногда, когда утверждение ложно, меня не информируют. Должен ли блок try catch идти с каждым утверждением? - person Dijkstra; 15.01.2013
comment
Я считаю это обходным путем, а не ответом на вопрос. - person Bohemian♦; 15.01.2013
comment
@Bohemian Почему ты так говоришь? - person Daniel Kaplan; 15.01.2013
comment
Я тоже ничего не нашел в документации. Итак, я посмотрел на код FutureTask :: innerRun (), который, похоже, его запускает. Он перехватывает все бросаемые объекты, а затем устанавливает внутреннее исключение. Утверждения можно бросить, поэтому их ловят ... - person Dijkstra; 15.01.2013
comment
Этот вопрос дает хорошее представление о том, когда использовать утверждение или использовать исключения. - person Laf; 16.01.2013
comment
@DanielKaplan Это имеет смысл о ScheduledFuture. Но теперь мне грустно, потому что я не могу помещать утверждения в свой код и ожидать, что они всегда будут сообщать мне об ошибках. - person Dijkstra; 16.01.2013
comment
@Dijkstra Что ж, если вы последуете моему совету, вы всегда будете в курсе. Просто раздражает то, что вы должны помнить о том, чтобы следовать моему совету, вместо того, чтобы это происходило автоматически :( При этом связь этого с утверждениями немного узкая: та же проблема возникает с исключениями во время выполнения. Я думаю, вам просто нужно будьте осторожны с методом schedule. - person Daniel Kaplan; 16.01.2013

Потоки ничего не делают с исключениями (как они могли? Что, если вы не используете STDERR или STDOUT, а регистрируете в базе данных, как Thread.run () может знать, где регистрировать исключение или кому его повторно отправить?)

Существует Thread.UncaughtExceptionHandler, который вы можете использовать, и когда вы это сделаете, ваши утверждения должны пройти через это.

Я полагаю, проблема в том, что механизм Assert использует исключения и, следовательно, должен следовать заранее определенным правилам для исключений. Лично я не возражал бы, если бы Assert просто напечатал дамп стека и выполнил выход (0), потому что вы в любом случае не должны перехватывать это исключение - но это то, что есть.

Обратите внимание, что вы также можете использовать Thread.setDefaultUncaughtExceptionHandler только один раз для всех потоков в вашем приложении (обратите внимание на «DEFAULT») ... это должно быть довольно хорошим общим решением.

import java.lang.*;

public class ThreadDemo {

   public static void main(String[] args) {

     Thread t = new Thread(new adminThread());

     t.setUncaughtExceptionHandler(new Thread.
     UncaughtExceptionHandler() {
        public void uncaughtException(Thread t2, Throwable e) {
           System.out.println(t2 + " throws exception: " + e);
        }
     });
     // this will call run() function
     t.start();

   }
}

class adminThread implements Runnable {

   public void run() {
      assert(false);
      throw new RuntimeException();
   }
} 

Когда я запускаю это «Обычно», это показывает, что было выброшено исключение RuntimeException. Когда я запускаю с параметром «-ea», отображается исключение AssertException. Попробуйте.

person Bill K    schedule 15.01.2013
comment
Все это звучит так, как будто это должно работать, но когда я попробовал, ничего не вышло. Хотелось бы, чтобы я мог вставить блок кода в комментарий, чтобы дать более подробную информацию. Я реализовал первую строку main () для вызова Thread.setDefaultUncaughtExceptionHandler, а impl просто говорит e.printStackTrace();. Однако по-прежнему нет вывода на консоль. - person Daniel Kaplan; 16.01.2013
comment
Вдобавок, если я устанавливаю точку останова на этом e.printStackTrace();, он не попадает в него. - person Daniel Kaplan; 16.01.2013
comment
Это своего рода соломенный ответ. В своем сценарии он использует запланированного исполнителя. В этом случае вся разница. - person Daniel Kaplan; 16.01.2013
comment
В этом случае ответ здесь даст вам выполнение: stackoverflow.com/a/2554845/12943 - person Bill K; 17.01.2013
comment
Да, это похожий сценарий. Я обратился к этому решению в своем ответе. - person Daniel Kaplan; 17.01.2013