0

I feel like this is a noobish question but I'm getting back into java so I'm a little stumped

I have a Player class that contains a public attack method() but for some reason when I try to call the method in the main class where I have created an instance of the Player class java says it can't find the method? I'm hoping someone can tell me what's wrong

I have tried rebuilding the class to make sure it was up to date... I don't see any other errors with other methods in the class.. so I'm not sure what the issue is??

Exception in thread "main" java.lang.NoSuchMethodException: org.example.Player.attack()
    at java.base/java.lang.Class.getMethod(Class.java:2227)
    at org.example.Main.main(Main.java:31)

here is my player class

package org.example;
import java.util.Random;


public class Player {
    public String name;
    public static int health;
    public int def;
    public int atk;
    private String[] domains = {"hauntings","findings","solutions","busters","slayers","defense","protection","powers","scary"};

    public Player(String name, int health, int atk, int def) {
        this.name = name;
        this.health = health;
        this.atk = atk;
        this.def = def;
    }

    // player will make an http request for a random site trying to find anything related to ghosts
    // if the response is 200 we assume the player has learned something new about ghosts and increased their knowledge
    // this in turn increases player atk power

    public void loseHealth(int amount){
        this.health = this.health-amount;
        System.out.println(this.name+" now has "+this.health+" health");
    }
   public void searchWebForAnswers () {
       Random random = new Random();
        int randomIndex = random.nextInt(this.domains.length);
        String randomUrl = domains[randomIndex];

        // if this website exists we will increase atk by 1
        if( randomUrl == "defense"){
            this.atk +=1;
        }
    }

    public void attack(Ghost ghost){
        Random rand = new Random();
        double attackPlusRoll = Math.floor(Math.random() *(4-1 +this.atk) + 1);
        if(this.atk > ghost.def && ghost.getIsTransparent() == false){
            ghost.setGhostHealth(this.atk-ghost.def);
        }else{
            System.out.println("the attack does no damage: player_atk:"+this.atk+"ghost_defense:"+ghost.def);
        }
    }

    public int getHealth(){
        return this.health;
    }
    public void setPlayerHealth(int val){
        this.health = val;
    }
}

here is my main class

package org.example;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Random;

public class Main {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        String[] playerMethods = {"attack", "searchWebForAnswers"};
        String[] ghostMethods = {"learnToSpeak", "sayBoo", "haunt","becomeTransparent","attack"};
        System.out.println("BEGIN THE HAUNT!");

        /*
         player will go first, then ghost
         a random action will be taken by whoever goes
         the turns continue until either the ghost is exorcised or the player dies
         */

        Player player = new Player("Luther", 10, 1, 3);
        Ghost ghost = new Ghost(20, 0, 3);

            while (player.health > 0 && ghost.health > 0) {
                Random random = new Random();

                // a random method is chosen each turn
                // this does not include methods like getHealth or methods used by the game to determine state

                    System.out.println("player is taking their turn");
                    int randomMethodIndex = random.nextInt(playerMethods.length);
                    String randomMethodName = playerMethods[randomMethodIndex];
                    Method method = Player.class.getMethod(randomMethodName);
                    if( randomMethodName == "attack"){
                        player.attack(ghost);
                    }else {
                        method.invoke(player);
                    }


                    System.out.println("ghost is taking their turn");
                    int randomMethodIndex2 = random.nextInt(playerMethods.length);
                    String randomMethodName2 = ghostMethods[randomMethodIndex];
                    Method method2 = Ghost.class.getMethod(randomMethodName2);
                    if( randomMethodName2 == "ghostAttack"){
                        ghost.ghostAttack(player);
                    }else {
                        method2.invoke(ghost);
                    }

            }
        if(player.getHealth() == 0){
            System.out.println("player has died");
            System.out.println(player.health);
        }else{
            System.out.println("congratulations you've exorcised the ghost");
            System.out.println("player "+player.health);
            System.out.println("ghost "+ghost.health);
        }
    }
};

here is a quick dump using a python script of all the files present in the local project

\simple-api\ghostgame\.idea\.gitignore
\simple-api\ghostgame\.idea\compiler.xml
\simple-api\ghostgame\.idea\dbnavigator.xml
\simple-api\ghostgame\.idea\encodings.xml
\simple-api\ghostgame\.idea\jarRepositories.xml
\simple-api\ghostgame\.idea\misc.xml
\simple-api\ghostgame\.idea\workspace.xml
\simple-api\ghostgame\.idea
\simple-api\ghostgame\pom.xml
\simple-api\ghostgame\src\main\java\org\example\GameActions.java
\simple-api\ghostgame\src\main\java\org\example\Ghost.java
\simple-api\ghostgame\src\main\java\org\example\Main.java
\simple-api\ghostgame\src\main\java\org\example\Player.java
\simple-api\ghostgame\src\main\java\org\example
\simple-api\ghostgame\src\main\java\org
\simple-api\ghostgame\src\main\java
\simple-api\ghostgame\src\main\resources
\simple-api\ghostgame\src\main
\simple-api\ghostgame\src\test\java
\simple-api\ghostgame\src\test
\simple-api\ghostgame\src
\simple-api\ghostgame\target\classes\org\example\GameActions.class
\simple-api\ghostgame\target\classes\org\example\Ghost.class
\simple-api\ghostgame\target\classes\org\example\Main.class
\simple-api\ghostgame\target\classes\org\example\Player.class
\simple-api\ghostgame\target\classes\org\example
\simple-api\ghostgame\target\classes\org
\simple-api\ghostgame\target\classes
\simple-api\ghostgame\target\generated-sources\annotations
\simple-api\ghostgame\target\generated-sources
\simple-api\ghostgame\target
monsterpiece
  • 739
  • 2
  • 12
  • 34
  • `NoSuchMethodException` means that in your ***RUNTIME*** environment the class exists but does not have that method. You may have recompiled your code but not deployed the `Player` class into your runtime location. We'd need to know how you're building and deploying your code to help further. – Jim Garrison Apr 11 '23 at 22:37
  • 1
    By the way [== is the wrong way to compare Strings](https://stackoverflow.com/questions/513832/how-do-i-compare-strings-in-java/513839#513839). – tgdavies Apr 11 '23 at 22:47
  • When you call `Class.getMethod` you also need to give the types of the parameters the method takes. – tgdavies Apr 11 '23 at 22:48
  • thanks @JimGarrison I have all the classes in the same package and I'm using maven as the build env by default, I'm just debugging this locally at the moment maybe I need to delete some folder specific to maven related to a prior build? – monsterpiece Apr 11 '23 at 22:51
  • @JimGarrison What you describe is a `NoSuchMethodError`. The `NoSuchMethodException` that OP is getting occurs when using reflection and asking for non-existant methods. Here, the correct answer is that OP needs an explanation about how the parameter types are an intrinsic part of a method's identity in java. – rzwitserloot Apr 12 '23 at 02:09

1 Answers1

1

Here's a simple example of calling a method, showing what happens when the parameters to getMethod aren't specified.

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Test {

    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Class clazz = Player.class;
        Method defend = clazz.getMethod("defend");
        Method attack = clazz.getMethod("attack", String.class);
        Player player = new Player();
        defend.invoke(player);
        attack.invoke(player, "Ghost");
        attack = clazz.getMethod("attack"); // throws
    }

}

class Player {
    public void attack(String thing) {
        System.out.println("Attacking " + thing);
    }
    public void defend() {
        System.out.println("Defending");
    }
}

defend has no parameters, so a simple getMethod("defend") works, but to get attack you need to specify the "signature" of the method, which includes the name and the parameter types.

The return type of the method is not part of the signature, which is why a class can't have two methods with the same name and parameter types even when the return type is different, so you don't need to give the return type when you call getMethod.

tgdavies
  • 10,307
  • 4
  • 35
  • 40
  • right @tgdavies that's why i'm creating an explicit condition for player.attack() since it has a parameter and I am passing that parameter in ``` Method method = Player.class.getMethod(randomMethodName); if( randomMethodName == "attack"){ player.attack(ghost); }else { method.invoke(player); }``` that's why I'm stumped as to why I'm still getting the error – monsterpiece Apr 11 '23 at 23:06
  • See my first comment -- you can't use `==` for `String` comparisons. Use `equals()`. – tgdavies Apr 11 '23 at 23:08
  • But your `getMethod` call will still throw an exception, because you are specifying a method signature that doesn't exist. – tgdavies Apr 11 '23 at 23:09
  • ahhhh the condition is evaluating to false I see let me play with that – monsterpiece Apr 11 '23 at 23:09
  • I understand now, here's a quick fix I'm sure I could condense this but I'll come back to that in a bit thanks! @tgdavis looks like it works now ``` if( randomMethodName.equals("attack")) { Method method = Player.class.getMethod(randomMethodName, Ghost.class); method.invoke(player, ghost); }else{ Method method = Player.class.getMethod(randomMethodName); method.invoke(player); } ``` – monsterpiece Apr 11 '23 at 23:15
  • That should work. – tgdavies Apr 11 '23 at 23:18
  • 1
    @monsterpiece Why not just run `if (randomMethodName.equals("attack")) player.attack(ghost);`? Reflection with hardcoded strings is pretty much always wrong. – rzwitserloot Apr 12 '23 at 02:10