Change variable value within anonymous class

Asked

Viewed 154 times

0

I would like to know why whenever within an anonymous class I try to change the value of an "external" variable it does not, change remains the same. There would be something similar that I could do that would give the same result?

I used the Thread as an example, but in any anonymous class it happens. (Including on Android)

public class Teste {

    public boolean umIgualUm = false;

    public Teste() {
        fazerAlgo();
    }

    public void fazerAlgo() {

        new Thread() {
            public void run() {
                if (1 == 1) {
                    System.out.println("Entra aqui");
                    umIgualUm = true;
                }
            }
        }.start();

        System.out.println(umIgualUm); //Exibe false

    }

}

Android example

public class UsuarioDAO {

    public boolean metodoDeuCerto = false;

    public UsuarioDAO(){}

    public boolean cadastrarUsuario(Usuario usuario) {

        ParseUser parseUser = new ParseUser();
        parseUser.setUsername(usuario.getNome());
        parseUser.setEmail(usuario.getEmail());
        parseUser.setPassword(usuario.getSenha());

        parseUser.signUpInBackground(new SignUpCallback() {
            @Override
            public void done(ParseException e) {
                if(e == null) { //Quer dizer que deu certo
                    metodoDeuCerto = true;
                }else{
                    e.printStackTrace();
                    metodoDeuCerto = false;
                }

            }

        });

        return metodoDeuCerto;

    }
}
  • I actually believe it’s because of the timing of things within the anonymous class happening. As in the example posted to startar the thread System.out.println runs right away and did not have time to finish the run process()

  • Are you sure this happens in case it is not "in a Thread"? In the use of "a Thread" it happens because when a thread System.out.println(umIgualUm); the line is executed umIgualUm = true; (may) not (have been)executed.

  • What other anonymous class could I test with?

  • On Android you can test for example on OnClickListener, assigned to a button.

  • I put the example of Android, what is really happening.

  • In this example the same happens. The method done() is called, by the implementation of signUpInBackground(), after the line return metodoDeuCerto; have been executed.

  • And how to do to give a return to the user whether it worked or not the registration? For since I can not do so, nor give the Return inside the method of the anonymous class (of course). How to do?

  • It depends on how you want to inform the user. However, whatever this way, it has to be done within the method done() or in a method called by him.

  • I needed to inform this user, return either to Exception or a Boolean, but I needed to return because it is not this class that will take care to warn the user with some message.

  • In this case pass Signupcallback to the method cadastrarUsuario() or, perhaps better, create a new interface/class to receive the result.

  • Would you have some way to wait for Signupcallback to finish so you can return?

  • If that’s what you really want, you can, see the documentation

  • Right, but I still don’t understand how to wait for my "main" thread to make this one wait. Sorry, I’m not as experienced with java.

  • That’s why I won’t tell you how. I don’t want to contribute to you starting to learn the wrong way :)

  • Well I did some research and I didn’t get anything so relevant. I wanted the idea to take the Thread of the User class and ask to wait or something. I have the idea of using the Join() method but I don’t know how exactly. Honestly, can you give me a hand, I’d like to do this soon, I’m getting distressed.

Show 10 more comments

2 answers

0

There are two problems here:

  1. The method start may take time to actually start the new thread. With this System.out may end up running first.

  2. Variables can be in thread-specific caches, and with that, different threads can see different values for the same variable at the same time. To get around this problem you can use synchronization or make the variables to be volatile (something that instructs JVM not to allow them to be kept in threadseparated caches). Another possibility is to use a AtomicBoolean.

The problem has no relation to the fact that the variable is being accessed from within the anonymous class. The problem is it’s being accessed from another thread.

If you see this problem happening when you use an anonymous class other than one Thread, request to show this code.

As for your second code, the methodsignUpInBackground creates a new thread to run what is in the anonymous class, which means that the method cadastrarUsuario will execute the return without waiting for the signUpInBackground be completed.

In fact, the method signUpInBackground() returns a Task<Void>.

What is the class Task according to the documentation?

Represents the result of an asynchronous Operation.

Translating into Portuguese:

Represents the result of an asynchronous operation.

What is an asynchronous operation?

It means that the process that starts it does not wait for its end, that will produce some result only in an uncertain future. That is, your method cadastrarUsuario is not actually registering a user, is just asking for a user to be registered and returning immediately. Note that this does not imply that the user was actually registered at any time. You would have to wait for the Task end up knowing this.

In both cases, the problem is that what appears in the body of the anonymous class, only runs long after the main method has already returned, and runs on another thread.

  • So I created this example only in Java, but I’m actually working on an Android project, which also uses an anonymous class (not Thread) and the same thing happens. The Java example I created to simplify. And for example, if I wanted to use it instead of Boolean, do this "Excpetion ex = null", then when I tried to modify it, it would continue as null, even if something else happened. I can send made on Android, using Parse.

  • Citei example of Android

  • @Hamon Reply edited.

  • Right, and how can I make the right comeback? The method even registers the user in the database, but does not change Boolean (we already have the explanation for this), but how would you make a return, in the best possible way? Without much loss of optimization

  • @Hamon, you will need to program asymptotically.

  • Sorry, I’m kind of layy, like?

Show 1 more comment

0

As they said before, you can’t change the value of a variable within the thread and then test it right away. Probably your test (if, println, or whatever) will be called before the thread is started.

One of the correct ways to work would be: (With your own example)

public class Teste {

    public Teste() {
        fazerAlgo();
    }

    public void fazerAlgo() {

        new Thread() {
            public void run() {                                       
                System.out.println("Entra aqui");

                if (1 == 1) {
                    metodoDeuCerto();
                } else {
                    metodoNaoDeuCerto();
                }
            }
        }.start();
    }

    public void metodoDeuCerto() {
        System.out.println("Deu certo!");
    }


    public void metodoNaoDeuCerto() {
        System.out.println("Nao deu certo!"); 
    }
}

(EDITED)

Android example:

public class UsuarioDAO {

    private SignupListener listener;

    public UsuarioDAO(){}

    public void cadastrarUsuario(Usuario usuario) {

        ParseUser parseUser = new ParseUser();
        parseUser.setUsername(usuario.getNome());
        parseUser.setEmail(usuario.getEmail());
        parseUser.setPassword(usuario.getSenha());

        parseUser.signUpInBackground(new SignUpCallback() {
            @Override
            public void done(ParseException e) {
                if(e == null) {
                   if ( listener != null ) {
                       listener.deuCerto();
                   }
                }else{
                   e.printStackTrace();
                   if ( listener != null ) {
                       listener.naoDeuCerto();
                   }
                }

            }

        });
    }

    public void setSignupListener(SignupListener signupListener) {
        this.listener = signupListener; 
    }

    public interface SignupListener {
        void deuCerto();
        void naoDeuCerto();
    }
}

// Example of Activity calling the User

public class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_activity); 

        Usuario usuario = new Usuario();
        usuario.setUserName("usuario");
        usuario.setEmail("[email protected]");
        usuario.setPassword("senha");

        UsuarioDAO dao = new UsuarioDAO();
        dao.setSignupListener(signupListener);
        dao.cadastrarUsuario(usuario);

    }

    private UsuarioDAO.SignupListener signupListener = new UsuarioDAO.SignupListener {
        @Override
        public void deuCerto() {
           //DO SOMETHING
        }

        @Override
        public void naoDeuCerto() {
           //DO SOMETHING
        }
    }
}
  • I needed a return on the method, because the method that receives this return warns the user

  • The problem is that you’re using a DAO class to do something you shouldn’t, which is signup. But I’m not going to get into that because that wasn’t the question. But that’s easily manageable by creating your own Software. I will edit the answer of the example with android for you to see how it could look.

  • But does the DAO not access the bank? Including the user’s registration in the.bank? Or am I doing it wrong?

  • The way the example looks, you seem to be doing something else in this signUpInBackground(), waiting for the answer of some api, etc. Anyway I updated the answer. This is one of the thousands of possible answers to your problem.

  • I thought it would be interesting to wait for this thread to finish, so I could return. I just can’t figure out how I can use Join() to keep the main thread waiting until the other one is done

  • You can not do this in the main thread of Android, this will only cause your application to stop responding and consequently may crash your app.

  • But then wait for the signup thread to finish so that it can return?

Show 2 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.