Passing a reference in Java

2020-07-11 08:59发布

I've this cards game, in which I store all the players in a List. To find out who's the player I want to act with, each player has a Card (I can get the card name), a name (I can get the player's name), but to be unique each player has an ID.

Now, at the beginning of my onCreate() method, I find, and assign a player of the list to a Player, Player clairvoyant:

public void initializeCharacters() {
    for (Player player : players) {
        if (player.getCardName().equals("Clairvoyant")) {
            clairvoyant = player;
        }
}

The game switch between Night and Day. The Clairvoyant's turn is during the night, and I used a switch to determine when's who's turn.

Now, before starting the Clairvoyant's turn, I check if he's alive or not, in negative case, I simply skip his turn:

case 2:
                    clairvoyant(); // Clairvoyant's turn
                    Toast.makeText(Game.this, String.valueOf(clairvoyant.getLifeStatus()), Toast.LENGTH_SHORT).show();
                    if(clairvoyant.getLifeStatus()) {
                    /* --- Let him choose ---*/
                        Intent intent = new Intent(this, ListPlayersClairvoyant.class);
                        Bundle bundle = new Bundle();
                        bundle.putSerializable("PLAYERS", (Serializable) players);
                        intent.putExtras(bundle);
                        startActivityForResult(intent, REQUEST_CLAIRVOYANT);
                    /* --------------------- */
                    }
                    nightInsideCounter++;
                    if(medium == null) {
                        nightInsideCounter++;
                    }
                    if(guard == null) {
                        nightInsideCounter++;
                    }
                    break;

And yet, I've added Toasts to see when I kill players if they're still alive, but even if the Player searched in the list is killed, the Clairvoyant player, previously created isn't.
So basically, the player in the list with the clairvoyant card, is dead; but the clairvoyant player, initialized before to have a reference to it before starting his turn, is still alive!

I don't understand why. Is anything I'm missing? Is that I've done a reference or not? In this case, how should I create a reference to it?


EDIT:

Now it all works fine, I have implemented the Parcelable interface on the Player Class and on the Card one too. The problem is when I get to the intent from ListPlayersVote Activity to the Game one (the main one), the App crashes and I get this error:

java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String java.lang.Object.toString()' on a null object reference at android.widget.ArrayAdapter.createViewFromResource(ArrayAdapter.java:401)

[...] // Many other links, skipped

Which is a simple NullPointer exception, but I get no links to my code for the error, instead I get all other links to other scripts (Probably already made, not by me) and this keeps me stuck, how am I suppose to fix the bug if I don't get any link to my code?


Resources

Vote Activity

Player Class

How I retrieve the ArrayLists passed, and check for their values (this is a StartActivityForResult() ):

    /* ------------------------------------------ */
    /* ---------- If they want to VOTE ---------- */
    /* ------------------------------------------ */
    if(requestCode == REQUEST_VOTE) {

        if(resultCode == Activity.RESULT_OK) {
            // Get the Player clicked
            ArrayList<Player> highestList = data.getParcelableArrayListExtra("HIGHEST");
            ArrayList<Player> highestList1 = data.getParcelableArrayListExtra("HIGHEST1");
            System.out.println("[5] The players of the first list are " + highestList.size() + ".");
            System.out.println("[6] The players of the second list are " + highestList1.size() + ".");

            // Add the most voted players to a signle List, highest
                highest.addAll(highestList);
                highestList.clear();

                highest.addAll(highestList1);
                highestList1.clear();

            // Write the names in chat
            write("Il villaggio ha i propri sospettati:");
            for (Player player : highest){
                write(player.getName());
            }
            System.out.println("[7] The players chosen are " + highest.size() + ".");
            highest.clear();

        } else if (resultCode == Activity.RESULT_CANCELED) {
            // Some stuff that will happen if there's no result
        }

    }

标签: java android
3条回答
欢心
2楼-- · 2020-07-11 09:40

The problem is when I get to the intent from ListPlayersVote Activity to the Game one (the main one), the App crashes and I get this error:

As I said in my comment, that exception related to the ArrayAdapter class happens when the adapter is used with a list of data that has null values in it. Looking through your code the culprit seems to be a Player object that doesn't have its fields initialized in the ListPlayersVote class. In that class, your current code will almost always introduce that non initialized Player object reference. In the ListPlayersVote's onCreate() callback you first add two Player objects to the holder lists, I'm assuming to have as a base for further calculations:

// remove the lines below , there's no need for them
// also simply doing new Player() will result in a Player which doesn't have    
// its fields initialized(and you don't update them later either)
highestList.add(new Player()); // is there a reason for adding this empty players?
highestList1.add(new Player());

Then in the onEntryClick() callback update your code to properly handle counting the players votes:

for (Player player1 : players){ // For each player
  // first of all you need to move the initialization for highest and highest1 INSIDE the
  // for loop to use the proper highest values at each iteration
  // With your current code the lists highestlist and highestlis1 each have
  // ONE Player object which is empty (because in the default constructor 
  // of Player you do nothing), you then use the empty initial Player in each list to
  // compare the vote count, this will mostly result in the first if  
  // clause(playerCount > highest) being triggered,
  // which will add the initial empty Player object to highestlist1
  int highest = highestList.size() == 0 ? -1 : highestList.get(0).getCount();// if the list is empty initialize it with -1 to signal the first player handled
  int highest1 = highestList1.size() == 0 ? -1 : highestList1.get(0).getCount();

  int playerCount = player1.getCount(); // Get his votes

    // you need to check for -1 to handle the case when you're dealing 
    // with the first player( which of course will have the highest 
    // count as there's no either player to compare it yet)
    if(playerCount > highest || highest == -1){ // If they're the highest so far, add it to the 1st list
        highestList1.addAll(highestList);
        highestList.clear();
        highestList.add(player1);
        // the same for highest1
    } else if (playerCount > highest1 || highest1 == -1){ // If they're the 2nd highest so far, add it to the 2nd list
        highestList1.clear();
        highestList1.add(player1);
    }else if(playerCount == highest){ // If they're equal to the current highest one, add it to the 1st list as well
        highestList1.add(player1);
    } else if (playerCount == highest1){ // If they're equal to the current 2nd highest one, add it to the 2nd list as well
        highestList1.add(player1);
    }
查看更多
闹够了就滚
3楼-- · 2020-07-11 10:01

It seems you are in search of a weak reference: if no other reference to the object exists, the weak reference is lost too. See Reference too.

WeakReference<Player> clairvoyant;

// Initialisation of clairvoyant from players:
// So do this whenever players is filled.
for (Player player : players) {
    if (player.getCardName().equals("Clairvoyant")) {
        clairvoyant = new WeakReference<>(player);
    }
}

// Everywhere where clairvoyant is inspected:
Player player = clairvoyant.get();
if (player != null) {
    // There is a clairvoyant player:
    ,,,
}

No experience with Android's java behaviour though.

查看更多
太酷不给撩
4楼-- · 2020-07-11 10:01

Once you serialize and deserialize an object, you won't have the same reference to it as you did before. You need to find another way of storing the player in memory or use a database to store and retrieve information about the player.

Edit:

Basically you have 3 options depending on your application's needs.

  1. You need to pass an object to an Activity in the same application. There is no need to persist the edits to the data between application restarts. Use a straight POJO and store the objects in that class. Make it a singleton that way when the new activity gets an instance of that class, it'll be using the same reference as the other activities.
  2. You need to pass an object between applications, or your edits need to persist between application restarts. Make your objects Parceable. Here there is a good example for implementing that.
  3. Persist you models to a database. Every edit you make to the object needs to be saved in the database and references to the object needs to be passed by ID. Then the receiving activity can pull that record from the database with that ID and it will have the most up to date info on it.

And as far as your NullPointerException. I assume that getID() returns a Integer type which is nullable. Whenever those primitive class wrappers are used in expressions they can throw a NullPointerException when unboxing. Always check for null when using those classes like Integer or Boolean.

查看更多
登录 后发表回答