Start learning free

LESSON 562 OF 569 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Notifiers in Java: Interfaces and implements

Practice O(n) Read the full explanation

An app tells its users about news in different ways: some get an email, some get a text message, an SMS. The code that sends the news shouldn't care which kind each user picked. It only needs to know that each one can send a message. That promise is an interface.

Write:

  • the interface Notifier, with one method, send(message), which returns the text that would be sent;
  • EmailNotifier(address), a notifier whose send returns "To ", the address, ": " and the whole message;
  • SmsNotifier(phone), a notifier whose send returns "SMS ", the phone number, ": " and only the first 20 characters of the message (a message of 20 characters or fewer is sent whole);
  • a method notifyAll(notifiers, message) in the class Solution, which gets a list of notifiers of any kind and returns the list of the texts they send, in the same order.

notifyAll must work through the interface: it calls send on each notifier and never checks which kind of notifier it is.

In Java, Notifier is an interface, and both notifier classes say implements Notifier.

The program creates the notifiers listed in the input, calls notifyAll once for each message, and prints each text it returns on its own line.

 

Example 1:

Input:
    notifiers: EmailNotifier("ana@mail.com"), SmsNotifier("0712345678")
    message: "Meeting at 5"
Output:
To ana@mail.com: Meeting at 5
SMS 0712345678: Meeting at 5
Explanation: The message has 12 characters, so the SMS sends all of it too.

Example 2:

Input:
    notifiers: SmsNotifier("5550100"), EmailNotifier("bob@work.org")
    message: "Your order has shipped today"
Output:
SMS 5550100: Your order has shipp
To bob@work.org: Your order has shipped today
Explanation: The message has 28 characters. The SMS keeps the first 20, "Your order has shipp";
the email sends all of it. The texts come in the order of the notifiers.

Example 3:

Input:
    notifiers: SmsNotifier("111"), SmsNotifier("222")
    messages: "Exactly twenty chars", "Exactly twenty chars!"
Output:
SMS 111: Exactly twenty chars
SMS 222: Exactly twenty chars
SMS 111: Exactly twenty chars
SMS 222: Exactly twenty chars
Explanation: notifyAll is called once for each message, with the same two notifiers.
The first message has exactly 20 characters and is sent whole. The second has 21, so its "!" is cut.

 

Constraints:

  • There are at most 50 notifiers. There can be none: then notifyAll returns an empty list.
  • A message has 1 to 100 characters: letters, digits, spaces and punctuation, with no space at its start or end.
  • Addresses and phone numbers have no spaces.
STEP 1 OF 11
    Ctrl + Enter to check

    JAVA · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Notifiers in Java, explained

    Problem overview

    An app tells users about news by email or by SMS. The code that sends the news shouldn't care which kind each user picked. It only needs to know that every notifier can send a message. In Java that promise is an interface.

    You write:

    • the interface Notifier, with one method, send(message), that returns the text that would be sent;
    • EmailNotifier(address), whose send returns "To ", the address, ": " and the whole message;
    • SmsNotifier(phone), whose send returns "SMS ", the phone, ": " and only the first 20 characters of the message (a message of 20 characters or fewer goes whole);
    • notifyAll(notifiers, message) in Solution, which returns the list of the texts the notifiers send, in the same order as the notifiers.

    With the notifiers EmailNotifier("ana@mail.com") and SmsNotifier("0712345678") and the message Meeting at 5, the texts are To ana@mail.com: Meeting at 5 and SMS 0712345678: Meeting at 5. The message has 12 characters, so the SMS isn't cut.

    Interfaces in Java

    An interface lists method headers without bodies. A class that says implements Notifier must provide a public send(String message) of exactly that shape, or the program doesn't compile. Methods in an interface are public automatically, so the class's version has to be declared public too. Leaving the modifier out is a compile error (attempting to assign weaker access privileges; was public), and so is leaving the method out (PushNotifier is not abstract and does not override abstract method send(String) in Notifier).

    A variable of the interface type can hold any object whose class implements it. That lets notifyAll take a list of Notifier and call send without asking what kind of notifier each element is. Java runs the send of the object's real class. A new kind of notifier, for example a push notification, would work with notifyAll without any change to it.

    Step by step

    1. Declare the interface method. Inside Notifier, write only the header String send(String message); ending in a semicolon.
    2. Give EmailNotifier a private field address, so only the class's own methods use it.
    3. Store it in the constructor with this.address = address;. The parameter and the field share a name, and this. picks the field.
    4. Write the email's send: "To " + address + ": " + message, marked @Override so the compiler checks it matches the interface.
    5. Do the same for SmsNotifier with a private field phone and its constructor.
    6. Cut the message for the SMS with message.substring(0, Math.min(20, message.length())).
    7. Write notifyAll: create an empty ArrayList<> named texts, loop over the notifiers with a for-each loop whose variable has the interface type Notifier, add notifier.send(message) to the list, and return it after the loop.

    The solution in Java

    interface Notifier {
        String send(String message);
    }
    
    class EmailNotifier implements Notifier {
        private String address;
    
        public EmailNotifier(String address) {
            this.address = address;
        }
    
        @Override
        public String send(String message) {
            return "To " + address + ": " + message;
        }
    }
    
    class SmsNotifier implements Notifier {
        private String phone;
    
        public SmsNotifier(String phone) {
            this.phone = phone;
        }
    
        @Override
        public String send(String message) {
            return "SMS " + phone + ": " + message.substring(0, Math.min(20, message.length()));
        }
    }
    
    class Solution {
        public List<String> notifyAll(List<Notifier> notifiers, String message) {
            List<String> texts = new ArrayList<>();
            for (Notifier notifier : notifiers) {
                texts.add(notifier.send(message));
            }
            return texts;
        }
    }

    Why Math.min is needed

    substring(0, 20) throws an exception when the string is shorter than 20 characters. For "Hi" it throws a StringIndexOutOfBoundsException. Taking the smaller of 20 and message.length() as the end index handles both cases: a long message is cut to 20 characters, and a short one is returned whole. The end index of substring is exclusive, so substring(0, 20) keeps exactly characters 0 to 19, which is 20 characters.

    Using it

    The list's type is List<Notifier>, the interface, so it can hold both classes side by side.

    Here is notifyAll on two notifiers of different kinds, with a message of 28 characters:

    List<Notifier> notifiers = new ArrayList<>();
    notifiers.add(new EmailNotifier("ana@mail.com"));
    notifiers.add(new SmsNotifier("5550100"));
    List<String> texts = new Solution().notifyAll(notifiers, "Your order has shipped today");
    System.out.println(texts);
    
    [To ana@mail.com: Your order has shipped today, SMS 5550100: Your order has shipp]
    

    The email keeps the whole message. The SMS keeps its first 20 characters, Your order has shipp, and the texts come back in the same order as the notifiers.

    Calling it more than once

    The same notifiers can be reused for several messages, because notifyAll keeps nothing between calls. With SmsNotifier("111") and SmsNotifier("222"), the 20-character message Exactly twenty chars produces SMS 111: Exactly twenty chars and SMS 222: Exactly twenty chars. The 21-character Exactly twenty chars! produces exactly the same two texts, because the ! is cut. The notifiers' fields (address and phone) are set once in the constructor and never change.

    Complexity

    notifyAll calls send once per notifier, and each send builds one short string (the message is at most 100 characters), so the time is linear in the number of notifiers, and the result list holds one text per notifier. With no notifiers, the loop doesn't run and the method returns an empty list, [].

    Edge cases and common mistakes

    • Exactly 20 characters. "Exactly twenty chars" has 20 characters, so the SMS sends it whole. "Exactly twenty chars!" has 21, so the SMS drops the !.
    • Shorter than 20. Without Math.min, substring throws an exception.
    • Forgetting public on send in a class that implements the interface. The compiler rejects it because the interface's method is public and an implementation can't be less visible.
    • Checking the class. Using instanceof inside notifyAll defeats the purpose of the interface. Call send and let each object answer.
    • Reordering the results. The texts must follow the order of the notifiers, which the for-each loop and add give you.
    • Cutting the email as well. Only the SMS is limited to 20 characters; the email sends the whole message.
    • Forgetting the separators. The prefix is "To " or "SMS ", then the address or phone, then ": " with a space after the colon.
    Keep going with Java500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free