
import java.util.Scanner;

/**
 * This program demonstrates two ways that we can use an interface with a single
 * abstract method.
 * <p>
 * This program requires two other files:
 * <ul>
 * <li> SAM.java, which contains the interface SAM. This interface has one
 * abstract (that is, undefined) method, named singleAbstractMethod.
 * <li> ImplementsSAM.java, which implements the SAM interface by having a
 * definition for singleAbstractMethod.
 * </ul>
 * <p>
 * In this program, we have a method named someMethod that's expecting to be
 * given an int value and a SAM object. It then calls the SAM's
 * singleAbstractMethod method with the int value as its argument.
 * <p>
 * The first way to do this, the traditional way, is to create a class that
 * implements the interface. So, our interface SAM that has a single abstract
 * method -- a single method that is missing its definition. We create the class
 * ImplementsSAM and make it implement SAM by giving it a definition for
 * singleAbstractMethod. Then we create a new ImplementsSAM object in the spot
 * where the program expects a SAM object to be used. (See someMethod below.)
 * <p>
 * The second, more modern and simpler way, is to create a lambda expression in
 * the place where the SAM object is supposed to be used. The computer is
 * expecting a SAM object, so when it sees the lambda expression it knows that
 * it must represent a SAM object. And since the SAM object has a single method
 * that still needs a definition, the lambda expression must be there to provide
 * that definition. It knows what kind of parameters the method expects, so it
 * matches up the name(s) before the arrow with the parameters, and then fills
 * in the body using the bit after the arrow. If necessary, it adds a return
 * command in front of the bit after the arrow.
 *
 * @author Mark Young (A00000000)
 */
public class UseSAM {

    public static final Scanner KBD = new Scanner(System.in);

    public static void main(String[] args) {
        System.out.println("\n"
                + "The method someMethod expects to be given "
                + "an int and a SAM. "
                + "It then passes\nthat int "
                + "to that SAM's singleAbstractMethod for processing. "
                + "Below are the\nresults....");
        pause();
        System.out.println("someMethod(10, new ImplementSAM());");
        System.out.println("----------------------------------------");
        someMethod(10, new ImplementsSAM());
        System.out.println("----------------------------------------");
        pause();
        System.out.println("someMethod(25, a -> ...long print command...)");
        System.out.println("----------------------------------------");
        someMethod(25, a -> System.out.println("\n"
                + "But that's not ALL!\n\n"
                + "If you put a lambda expression in a place "
                + "where Java is expecting a SAM\n"
                + "then Java knows it must stand "
                + "for a definition of that method! Thus:\n\n"
                + "    a -> System.out.println(a * a)\n\n"
                + "generates an object with this method definition:\n\n"
                + "    public void singleAbstractMethod(int a) {\n"
                + "        System.out.println(a * a);\n"
                + "    }\n\n"
                + "Like THIS:"));
        System.out.println("----------------------------------------");
        System.out.println("someMethod(4, a -> System.out.println(a * a));");
        System.out.println("----------------------------------------");
        someMethod(4, a -> System.out.println(a * a));
        System.out.println("----------------------------------------");
        pause();
        System.out.println("someMethod(10, a -> "
                + "System.out.println(\"Whatever!!!\"));");
        System.out.println("----------------------------------------");
        someMethod(10, a -> System.out.println("Whatever!!!"));
        System.out.println("----------------------------------------");
    }

    /**
     * This is a method that expects to be given an int and a SAM object. The
     * SAM object has a singleAbstractMethod that expects to be given an int
     * value. This method gives the int to the SAM object's
     * singleAbstractMethod.
     *
     * @param myNumber the number to give to {@code sam}
     * @param sam the SAM object to give {@code myNumber} to
     */
    private static void someMethod(int myNumber, SAM sam) {
        sam.singleAbstractMethod(myNumber);
    }

    /**
     * Prompt the user and wait for them to press the enter key.
     */
    private static void pause() {
        System.out.print("\n...press enter...");
        KBD.nextLine();
        System.out.println();
    }

}
