Search code examples
javaswingtimeractionlistener

I want to take the return value from an object's function after 5 second in java


I'm making a basic card game that human user can play with computer or computer play with computer in java. I wanna take a selection as a random number from a selection function in the Computer Player class that extends my Player class. But I wanna take answer after 5 seconds from calling selection function. Also I must take a answer from selection function in Human Player class with mouse listener.

I create a waiter timer that start when calling my selection function.

Timer waiter = new Timer(5000,this);
boolean is_waiting;

public int select_cart() {
        waiter.start();
        is_waiting = true;
        Random r = new Random();
        int random = r.nextInt(getCartNumber()) + 1;
        int selection = random;
        while(is_waiting){
            System.out.println("Computer is selecting...");
        }
        return selection;
    }

@Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == waiter){
            is_waiting = false;
            waiter.stop();
        }
    }

I expect that select_cart function return a value after 5 second but this code print "Computer is selecting..." to console in endless loop.


Solution

  • If select_cart is been called from the Event Dispatching Thread

    Then you need to change your approach, as the while-loop is blocking the Event Dispatching Thread, meaning that the Timer's actionPerformed method will never be called.

    In this case, a better approach would be to allow the actionPerformed method to actually trigger the result, but, you'll need to employ a observer pattern to make it work

    Start by defining a callback interface, something like...

    public interface CartSelection {
        public void cartSelection(int value);
    }
    

    Then you need to change the code to make use of this callback and move the core functionality to actionPerformed, for example...

    Timer waiter = new Timer(5000,this);
    CartSelection callback;
    
    public void select_cart(CartSelection callback) {
        this.callback = callback;
        waiter.start();
    }
    
    @Override
    public void actionPerformed(ActionEvent e) {
        if(e.getSource() == waiter){
            waiter.stop();
            Random r = new Random();
            int random = r.nextInt(getCartNumber()) + 1;
            int selection = random;
            callback(selection)
        }
    }
    

    Then you could call select_cart something like...

    select_cart(new CartSelection() {
        @Override
        public void cartSelection(int value) {
            // Carry on...
        }
    });
    

    as an example.

    If the cart is been filled by some other process, then you might consider using different approaches, such as using Lock objects to "wait" till the filling process completed.

    Equally, you could use a SwingWorker to fill the cart and when it's completed, publish the results to be consumed via some other mechanism.

    If select_cart is been called from another thread

    Now, if you're calling select_cart from a different thread (other then the EDT), then you don't actually need the Timer at all and could just do ...

    public int select_cart() {
        Random r = new Random();
        int random = r.nextInt(getCartNumber()) + 1;
        int selection = random;
        try {
            Thread.sleep(5000);
        } catch (InterruptedException ex) {
        }
        return selection;
    }