Given that Cyrille has already given some good advice, here's a more 'learned' solution, showing some patterns and possibilities to watch out for:
public class App {
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
Testing.collectAndReport(System.out, System.in);
}
}).start();
}
}
The first thing that you should know is that it's always best to isolate pretty much everything from your main(...)
method. Among other things, this means that it can be easy to include your actual application in some other framework, simply by saying "instantiate this class". Parsing args
as needed should be fine.
For the same reason, it's best for the main method to not 'run' your actual application, either. This is more for when dealing with Swing
, where you're supposed to use the 'event-dispatch' thread, but here results in (directly) creating a thread to start.
Thread
should not be sub-classed, so an anonymous inner sub-class of Runnable
(an interface) is used. The one required method, run()
, contains the actual 'startup' code of the application.
It's always best to 'inject' dependencies, such as input/output streams (System.in
and System.out
). For one thing, this allows other implementations to be swapped out as necessary; in our 'application framework' example, this means I could use a fancy text editor, instead of requiring use of the console.
public final class Member {
private final String firstName;
private final String lastName;
public Member(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
@Override
public String toString() {
return String.format("%s\t%s", firstName, lastName);
}
}
Learn how to make good use of 'value' types - small classes, comprising no more than a few primitives. Unless there are very good reasons, these should be 'Immutable' (although you're allowed to 'cheat', sortof) - among other things, doing so makes multi-threaded code much simpler. Use things like an overrideable toString()
method to be able to get an 'understandable' output.
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.Scanner;
public class Testing {
public static void collectAndReport(PrintStream out, InputStream in) {
final Scanner input = new Scanner(in);
// This count is because you want to print out the index.
int count = 1;
for (Member member : getMembers(out, input)) {
out.printf("%d\t%s\t\n", count++, member);
}
}
private static Iterable<Member> getMembers(final PrintStream out, final Scanner input) {
return new Iterable<Member>() {
public Iterator<Member> iterator() {
out.println("Number of Members:");
final int numberOfMembers = input.nextInt();
return new Iterator<Member>() {
int member = 0;
public void remove() {
throw new UnsupportedOperationException("Not Implemented");
}
public Member next() {
out.println("What is the first name?");
final String firstName = input.next();
out.println("What is the second name?");
final String lastName = input.next();
return new Member(firstName, lastName);
}
public boolean hasNext() {
return member++ < numberOfMembers;
}
};
}
};
}
}
... Learn how to cheat. Computer programming is largely about being able to think in abstractions. Learn what abstractions your language supports, and how to use them. Also learn the standard libraries (and the more commonly used public ones), and what they enable.
For instance, by implementing Iterable<Member>
like this, I can work with the results as a collections much easier. By implementing Iterator<Member>
, I can trivially modify this code to accept 'early exit' from the member creation, or even allow 'infinite' entries (by changing how hasNext()
works).
All code compiles and runs on my local JVM/eclipse instance.