Start learning free

LESSON 559 OF 569 · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

Shipping Options in Java: Inheritance and Overriding

Practice O(1) Read the full explanation

An online shop offers three ways to ship a parcel. Standard shipping is the basic one. Express and Economy are kinds of shipping too: they answer the same two questions, what the option is called and what it costs, but each in its own way. So each one overrides the parent's methods with its own versions.

Implement the class Shipping, the standard option:

  • name() returns "Standard".
  • cost(weight) returns 5 + weight: a base price of 5, plus 1 for each kilogram.

and two classes that inherit from Shipping and override both methods:

  • Express: name() returns "Express", and cost(weight) returns 10 + 2 * weight.
  • Economy: name() returns "Economy", and cost(weight) returns 3 plus half the weight, rounded down to a whole number: half of 7 is 3.

The input lists parcels, one per line: the option (standard, express or economy) and the weight. For each line, the code that tests your classes creates that option and prints <name>: <cost> from its name() and cost(weight). The two calls are the same for every option (where variables have types, through a variable of the parent type Shipping); because the methods are overridden, each option answers with its own version. The starter code spells these names the way your language does.

 

Example 1:

Input:
    standard 4
    express 4
    economy 4
Output:
    Standard: 9
    Express: 18
    Economy: 5
Explanation: The same parcel of 4 kilograms costs 5 + 4 = 9 with Standard, 10 + 2 × 4 = 18 with Express
and 3 + 2 = 5 with Economy (half of 4 is 2).

Example 2:

Input:
    standard 0
    express 0
    economy 0
Output:
    Standard: 5
    Express: 10
    Economy: 3
Explanation: With a weight of 0, only the base prices are left.

Example 3:

Input:
    economy 7
    economy 1
    standard 7
    express 7
Output:
    Economy: 6
    Economy: 3
    Standard: 12
    Express: 24
Explanation: Economy halves the weight and rounds down: half of 7 is 3, so 3 + 3 = 6, and half of 1 is 0, so 3 + 0 = 3.

 

Constraints:

  • 0 <= weight <= 1000, a whole number of kilograms.
  • At most 100 parcels per test case.
STEP 1 OF 6
    Ctrl + Enter to check

    JAVA · OBJECT-ORIENTED PROGRAMMING - CHALLENGES

    Shipping Options in Java, explained

    Problem overview

    A shop ships parcels in three ways. Every option answers the same two questions: what is it called, and what does it cost for a parcel of a given weight? The answers differ. You write three classes: Shipping (the standard option) and two subclasses, Express and Economy, that inherit from it and override both of its methods.

    Classname()cost(weight)
    Shipping"Standard"5 + weight
    Express"Express"10 + 2 * weight
    Economy"Economy"3 + weight / 2

    For a 4 kilogram parcel the costs are 9, 18 and 5. The lesson's tests create an option for each input line and print name(): cost, so the first example prints Standard: 9, Express: 18 and Economy: 5.

    How inheritance and overriding work

    In Java, class Express extends Shipping makes Express a kind of Shipping. It gets the parent's methods for free, and it can replace them by declaring a method with the same name and parameters. Marking the replacement with @Override asks the compiler to check that the parent really has such a method. If you misspell the name or change a parameter type, the compiler reports an error instead of quietly creating a new, unrelated method.

    Java decides which version to run from the object, not from the variable. A variable of type Shipping can hold an Express, and calling cost on it still runs the express version:

    Shipping s = new Express();
    System.out.println(s.name() + " " + s.cost(3));
    

    This prints:

    Express 16
    

    That is why the code that uses your classes can hold every option in the same kind of variable and call the same two methods: each object answers with its own version.

    Step by step

    1. Write Shipping.name so it returns "Standard".
    2. Write Shipping.cost so it returns 5 + weight: a base price plus 1 per kilogram.
    3. Override name in Express with the same header, marked @Override, returning "Express".
    4. Override cost in Express to return 10 + 2 * weight. Without it, an express parcel would get the parent's price.
    5. Override name in Economy to return "Economy".
    6. Override cost in Economy to return 3 + weight / 2.

    Each subclass changes only itself. Overriding in Express does not affect Economy or Shipping.

    The solution in Java

    class Shipping {
        public String name() {
            return "Standard";
        }
    
        public int cost(int weight) {
            return 5 + weight;
        }
    }
    
    class Express extends Shipping {
        @Override
        public String name() {
            return "Express";
        }
    
        @Override
        public int cost(int weight) {
            return 10 + 2 * weight;
        }
    }
    
    class Economy extends Shipping {
        @Override
        public String name() {
            return "Economy";
        }
    
        @Override
        public int cost(int weight) {
            return 3 + weight / 2;
        }
    }

    Nothing else is needed: no constructors, because none of the classes keeps any data, and no fields. Each cost is a plain formula of the weight.

    Integer division in Economy

    Both weight and 2 are int, so weight / 2 drops the remainder instead of producing a fraction. For a weight of 7, weight / 2 is 3, so the cost is 6. For a weight of 1 it is 0, so the cost is 3.

    Using the three classes together

    The array below has the type Shipping[], yet it holds one object of each class. The loop never asks which class an element belongs to. It calls name() and cost(7), and every object runs its own version:

    Shipping[] options = { new Shipping(), new Express(), new Economy() };
    for (Shipping option : options) {
        System.out.println(option.name() + ": " + option.cost(7));
    }
    
    Standard: 12
    Express: 24
    Economy: 6
    

    This is the practical reason for overriding. The alternative is one class with an if chain on a "kind" field, and every new shipping option would mean editing that chain. With subclasses, a new option is a new class, and the code that loops over parcels stays the same. Because Express and Economy are also Shipping objects, they fit anywhere a Shipping is expected.

    Inherited, overridden, or extended

    A subclass that overrides nothing simply behaves like its parent. A subclass can also override a method and still reuse the parent's version through super. In this example, Pickup keeps both methods as they are, and GiftWrapped adds 2 to the parent's price:

    class Pickup extends Shipping {
    }
    
    class GiftWrapped extends Shipping {
        @Override
        public int cost(int weight) {
            return super.cost(weight) + 2;
        }
    }
    

    For a weight of 4, a Pickup object reports Standard and costs 9, while a GiftWrapped object reports Standard and costs 11. The lesson's Express and Economy don't use super, because their prices don't build on the standard one.

    Edge cases

    • Weight 0. Only the base prices are left: 5, 10 and 3.
    • Odd weights in Economy. The half is rounded down, so weights 6 and 7 cost the same: 3 + 3 = 6.
    • Large weights. The weight is at most 1000, so the largest result, 10 + 2 * 1000, is 2010 and fits easily in an int.

    Testing

    Trying every option on a few weights gives:

    WeightStandardExpressEconomy
    05103
    16123
    49185
    712246

    Common mistakes

    • Forgetting to override cost. If Express only overrides name, express parcels print the right name with the standard price.
    • Changing the header. An override needs the same name and parameter list. cost(double weight) would be a different method, and @Override turns that slip into a compile error.
    • Dividing after multiplying in Economy. The rule is 3 + weight / 2, with the division applied to the weight alone and then added to 3.
    • Misspelling the names. The returned text is printed as it is, so "express" in lower case does not match "Express".
    • Making the methods private or static. An override must be callable through the parent type, so keep public and don't add static. A narrower access level is a compile error, because a subclass may not make a method less visible than the parent's.
    Keep going with Java500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free