import java.util.*; public class OptionExample { private static final int NUM_JOBS = 10; private static class Job { int salary; Job(int salary) { this.salary = salary; } } public static void main(String[] args) { Job[] jobs = new Job[NUM_JOBS]; // Make jobs each with the salaries: // {10000, 20000, 30000, 40000, 50000, 60000, 70000, 80000, 90000, 100000} for (int i = 0; i < jobs.length; i++) { jobs[i] = new Job((i + 1) * 10000); } try { // Find an instance of a high paying job and print it out Job highPayingJob = getHighPayingJob(jobs, 50000); System.out.println(highPayingJob.salary); // Now what happens if we can't find a high paying job? What if the client doesn't // read the documentation or if the implementor doesn't specify the behavior for when // a high paying job cannot be found? // // Looking at the implementation of getHighPayingJob, notice that it returns null... highPayingJob = getHighPayingJob(jobs, 10000000); // highPayingJob is null because the threshold is too high!!! System.out.println(highPayingJob.salary); } catch (NullPointerException e) { // BAM it gets into this catch block because getHighPayingJob returns null! System.out.println("The job is null!"); } // Well how do we get rid of this issue? // One easy way to combat this solve this is to check if the job returned is not null as so: Job highPayingJob = getHighPayingJob(jobs, 10000000); if (highPayingJob != null) { // Guarenteed that the method returned not null! System.out.println(highPayingJob.salary); } // But knowing what we've learned from SML is there a cleaner way to do this? If a client // forgets to put just one != null check, then the programs crashes! // So how can an implementor of a method tell the client that it is possible to return // a 'null' or a value that signifies that there was some sort of error in processing // the array of jobs? We can use the Optional introduced in Java 8. Optional highPayingJobOpt = getHighPayingJobImproved(jobs, 50000); if (highPayingJobOpt.isPresent()) { // just like isSome in SML // The .get() call is just like valOf System.out.println(highPayingJobOpt.get().salary); } // But isn't like this a neater way of just doing != null checks? // Yep! Unfortunately, unlike SML though, we don't have any good way // to use pattern matching to get the value out the SOME constructor. // // You can also use the ifPresent method on the Optional and pass it in a // lambda function to have it execute the print statement if and only if // the highPayingJob is not empty. // // Don't worry about this, we'll cover lambdas next week! highPayingJobOpt.ifPresent(job -> System.out.println(job.salary)); // If you want to do something in the case of an empty optional, unfortunately // Java 8 doesn't have that quite yet. But, when Java 9 comes out this summer, // it will be possible to do this: // // highPayingJobOpt.ifPresentOrElse(job -> System.out.println(job.salary), // () -> System.out.println("No job!")); // // Where the second parameter, the other lambda, is dealing with the case // where there is no job present. } /* * If there's a job that immediately is greater than the threshold, then return * it immediately. * * @param jobs The array of jobs to be processed. * @param threshold The threshold to have the salary exceed. * @return A high paying job greater than the threshold. */ public static Job getHighPayingJob(Job[] jobs, int threshold) { for (int i = 0; i < jobs.length; i++) { if (jobs[i].salary > threshold) { return jobs[i]; } } return null; } public static Optional getHighPayingJobImproved(Job[] jobs, int threshold) { for (int i = 0; i < jobs.length; i++) { if (jobs[i].salary > threshold) { return Optional.of(jobs[i]); // Same as SOME(jobs[i]) } } return Optional.empty(); // Same as NONE } }