5
\$\begingroup\$

I have made a Project Euler runner in order to facilitate the running of the various problems. I have never used reflection before, and I believe that I have found a good use for reflection within this project.

My question boils down to this:

  1. Is this an appropriate use of reflection? Or am I approaching this the wrong way?
  2. Is the static import of System wrong in this case?

package ProjectEulerRunner;

import static java.lang.System.*;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Scanner;

public class ProjectEulerRunner 
{
    private static final int NUM_OF_PROBLEMS = 20;
    private static final Scanner scan = new Scanner(in);
    public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, IOException 
    {
        for(;;)
        {
            out.println("Welcome to the Project Euler problem solver!");
            out.println("==========================================================");
            out.println("Total Number of Problems: " + NUM_OF_PROBLEMS);
            out.println("==========================================================");
            out.println();
            out.println("What would you like to do?");
            out.println();
            out.println("(1) Run all Project Euler problems.");
            out.println("(2) Run a specific problem number. Between 1 and " + NUM_OF_PROBLEMS);
            out.println("(3) Get all problem descriptions.");
            out.println("(4) Get a specific problem description.");
            out.println("(5) View all incomplete problems");
            out.println("(6) Basic problem statistics. (Percentage Correct, etc.)");
            out.println("(7) Advanced problem statistics. (Average Run Times, Collated Data, etc.");
            out.println("(0) Exit");
            out.println();
            out.print("Your choice: ");

            int input = scan.nextInt();

            switch(input)
            {
                case 0:
                    out.println("Exiting...");
                    exit(0);
                    break;
                case 1:
                    out.println("Running all problems...");
                    runAllProblems();
                    break;
            }
        }

    }

    private static void runAllProblems() throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, IOException 
    {
        for(int i = 1; i <= NUM_OF_PROBLEMS; i++)
        {
            Class<?> cls = null; 

            String num = EnglishNumberToWords.convert(i);
            num = capitalize(num);

            try {
                cls = Class.forName("Problems.Problem" + num);
            }
            catch(ClassNotFoundException e) {
                out.println("Problem " + EnglishNumberToWords.convert(i) + " not started yet!");
                out.println();
            }

            if(cls != null)
            {
                Object obj = cls.newInstance();
                Method method = cls.getDeclaredMethod("isCorrect");

                Object ret = method.invoke(obj);
                out.println(cls + ", Completed? (" + ret + ")");

                if((boolean)ret != false)
                {
                    method = cls.getDeclaredMethod("run");
                    Object run = method.invoke(obj);

                    out.println(run);
                    out.println();
                }
                else
                {
                    out.println();
                }
            }           
        }

        out.println("Press any key to return to the menu!");
        in.read();
    }

    private static String capitalize(String line)
    {
      return Character.toUpperCase(line.charAt(0)) + line.substring(1);
    }
}

Notes:

  1. Yeah, I know that I should be handling exceptions with more grace, but this is just to get things up and running at the moment.

  2. The main section I am concerned about at the moment is the runAllProblems() method.

  3. If anyone is confused by the Scanner(in) or numerous out.println's. I statically imported java.lang.System in order to shorten a bit of the code. Is this ok?

  4. Sadly, the code is uncommented. But I hope it is understandable. If it is not, I can comment or change things accordingly.

\$\endgroup\$
  • \$\begingroup\$ To explain my methodology to everyone: I am trying to avoid having an ArrayList<Problem> problems as that would mean I would have to go through EVERY problem and instantiate it with x = new Problems.Problem<InsertNumberHere>, followed by problems.add(x). That is why I am trying to use reflection to avoid having to type over 400+ new statements. Avoids looping as well. Am I mad? \$\endgroup\$ – Brendan Lesniak Oct 28 '14 at 4:58
  • \$\begingroup\$ I'd suggest looking into System.out.printf. It's much cleaner in a lot of cases. \$\endgroup\$ – raptortech97 Oct 29 '14 at 16:12
  • \$\begingroup\$ I agree, I just didn't need formatted output when I was initially putting this together. \$\endgroup\$ – Brendan Lesniak Oct 29 '14 at 18:37
  • \$\begingroup\$ you're just like me i also implemented reflection in java for project euler ... \$\endgroup\$ – RE60K Jun 10 '15 at 18:19
5
\$\begingroup\$

There are two concerns here of significance.

First up, I am not certain that reflection is the right tool for the job. You've done it in an effective (if messy) way, but a better approach would be to have each problem implement an interface, something like:

public interface ProblemSolver {
    public int getID();
    public void solve();
}

Now, you can use that to get the ID and solution, once you have the instance you need.

To get the instance, I would go the traditional route of adding instances to an array:

private static ProblemSolver[] SOLVERS = {
    new Problem1Solver(),
    new Problem2Solver(),
    ....
} 

Then, if you want to, you can add them to a structure like a TreeMap, that will sort them for you. Something like:

SortedMap<Integer, ProblemSolver> mySolvers = new TreeMap<>();
for (ProblemSolver ps : SOLVERS) {
    mySolvers.put(ps.getID(), ps);
}

Once they are in that Map, it would be easy to locate, and find things.

If you need to use reflection, I would still use a Map-like structure, but would only use it to locate the class definitions, and then still have the interface that each class uses. Only create instances that implement the interface. That way you can just use a system like (excluding exceptions)...:

try {
    Class eulerclass = Class.forName(prefix + number + suffix);
    if (eulerclass.isInstance(ProblemSolver.class)) {
        ProblemSolver instance = ProblemSolver.class.cast(eulerclass.newInstance());
        mySolvers.put(instance.getId(), instance);
    }
} .....

Once you have the class instances in the map, you are singing ;-)

That's enough for the reflection, but what about the rest?

Your runAllProblems() does far too much. At a minimum I would expect a method that contains:

public void runEuler(ProblemSolver problem) {
    System.out.println("Running problem " + problem.getID());
    try {
        problem.solve();
        System.out.println("Problem " + problem.getID() + " PASSED!");
    } catch (RuntimeException re) {
        // expect no checked exceptions, runtimes only.
        System.out.println("Problem " + problem.getID() + " FAILED!");
        re.printStackTrace();
    }
}

Then, I expect your code to be broken in to one part that builds up the available tests, and another part that loops through what's available, and runs it.

You can then, as you extend your code, use these smaller methods to solve the smaller activities.

\$\endgroup\$
  • \$\begingroup\$ a better approach would be to have each problem implement an interface Currently, every Problems.Problem<x> extends the base abstract class Problem. This was probably a bad spot for the use of an abstract class, an interface makes more sense. \$\endgroup\$ – Brendan Lesniak Oct 28 '14 at 16:06
3
\$\begingroup\$

If anyone is confused by the Scanner(in) or numerous out.println's. I statically imported java.lang.System in order to shorten a bit of the code. Is this ok?

Considering it only saves a few characters per line, I don't think it'll help. As such, it may not be worth importing it like that. Just try to cut down on the output if you really want to simplify that section.

Miscellaneous:

  • Consider wrapping those long line of exceptions from both functions or throw a lower number of exceptions (if possible) instead. That's too much side-scrolling.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.