-1

i'm having this problem and been stucked on this for hours. Im fairly new here and hope you guys are able to assist.

I thought the problem was with declaring the array or something but i cant seem to fix it.

My idea was to split the codes into three different parts to construct, display and the main method but apparently along the way there was an error.

private static void constructArray(Customer [] c){
    c = new Customer[3];
    ArrayList<ItemInfo> cust1 = new ArrayList<>();
    cust1.add(new ItemInfo("Mutton", 1, 19.85));
    cust1.add(new ItemInfo("Stationery",2,17.66));
    
    ArrayList<ItemInfo> cust2 = new ArrayList<>();
    cust2.add(new ItemInfo("Fishball",1,12.07));
    cust2.add(new ItemInfo("T Bone Steak",3, 19.20));
    cust2.add(new ItemInfo("Stationery", 2, 12.25));
    
    ArrayList<ItemInfo> cust3 = new ArrayList<>();
    cust3.add(new ItemInfo("Crab", 2, 14.08));
    
    c[0] = new Customer(cust1, "cheque");
    c[1] = new Customer(cust2, "cash");
    c[2] = new CustomerPlus(cust3, "Paynow", true);
}

private static void displayArray(Customer [] c){
     c = new Customer[3];
     for(Customer cust : c){
        if(cust instanceof CustomerPlus){
            CustomerPlus plus = (CustomerPlus) cust;
            plus.addItem(new ItemInfo("Cigarette",2,12.96));
            System.out.printf("Mode of payment:%s%nInvoice no:%04d%n%n", plus.getPaymentmode(), plus.getNumber());
            System.out.printf("%s%19s%10s%13s%n","Item","Quantity","Price","Sub Total");
            System.out.println(plus);
            System.out.printf("Total payment: %.2f%n", plus.computePayment());
            if(plus.getAdult())
               System.out.println("Customer: adult");
            System.out.println("-----------------------------------------");
               
        }

        else{
           System.out.printf("Mode of payment:%s%nInvoice no:%04d%n%n", cust.getPaymentmode(), cust.getNumber());
           System.out.printf("%s%19s%10s%13s%n","Item","Quantity","Price","Sub Total");
           System.out.println(cust);
           System.out.printf("Total payment: %.2f%n", cust.computePayment());
           System.out.println("-----------------------------------------");
        }
    }

}


public static void main(String[] args){
    main m = new main();
    Customer [] c = new Customer[3];
    constructArray(c);
    displayArray(c);
    
   
    
}
  • 1
    You are overwriting the array which comes in as the parameter `c` with a new empty array. You do that in both the `displayArray()` and `constructArray()` methods. Why do you do that? – Progman Jun 03 '21 at 13:31
  • In the second line of each method you're overriding the array that was passed as an argument with: `c = new Customer[3];`. – Nir Alfasi Jun 03 '21 at 13:32
  • oh initially i thought the problem was because the length was not established hence I wrote that part. – Izzat Bin Ismail Jun 03 '21 at 13:33
  • 1
    since you are only using static methods you don't need to do main m = new main(); m is not used anywhere as you can see. Assuming your class is called 'main', normally class names start with uppercase in Java, so 'Main' – Adriaan Koster Jun 03 '21 at 13:35
  • @AdriaanKoster thank you, i''ll keep that in mind. – Izzat Bin Ismail Jun 03 '21 at 13:36
  • 1
    Small side suggestion. You might want to get in the practice of [avoiding using instanceof](https://armedia.com/blog/instanceof-avoid-in-code/) since it's considered a code smell. – Tim Hunter Jun 03 '21 at 13:43

2 Answers2

1

It's a tricky one, but the issue is this line:

Customer [] c = new Customer[3];

Your code isn't instantiating three new Customer objects. It's merely allocated memory space for an array that holds three Customer objects. You have to loop through the array and instantiate the objects manually.

Customer [] c = new Customer[3];
for(int i = 0; i < c.length; ++i){
   c[i] = new Customer();
}
Yserbius
  • 1,375
  • 12
  • 18
  • 1
    Thank you i was able to rectify with your help and the others. Really appreciate it. – Izzat Bin Ismail Jun 03 '21 at 13:35
  • This answer is actually wrong. You don't have to loop, you can also assign the three values directly as the existing code is already doing. The problem is that the array is re-initialized in each method, causing the array values to be null, leading to an NPE – Adriaan Koster Jun 03 '21 at 14:12
1

Here is ultimately the cause of the NPE:

private static void displayArray(Customer [] c){
     c = new Customer[3];
     for(Customer cust : c){

You are initializing c as a new, empty array of Customers. All positions in this new array will have value null.

Then you try to iterate over them and access the fields of each customer. This will fail because the values are null.

Here a refactored version of your code:

public class Main {

    // methods which create a value should preferably return
    // their result rather than updating their input
    // for reasons of code readability
    private static Customer[] constructArray() {
        Customer[] customers = new Customer[3];

        // add the items directly to the customer instead of creating a list first
        customers[0] = new BasicCustomer("cheque");
        customers[0].addItem(new ItemInfo("Mutton", 1, 19.85));
        customers[0].addItem(new ItemInfo("Stationery", 2, 17.66));

        customers[1] = new BasicCustomer("cash");
        customers[1].addItem(new ItemInfo("Fishball", 1, 12.07));
        customers[1].addItem(new ItemInfo("T Bone Steak", 3, 19.20));
        customers[1].addItem(new ItemInfo("Stationery", 2, 12.25));

        customers[2] = new PlusCustomer("Paynow", true);
        customers[2].addItem(new ItemInfo("Crab", 2, 14.08));

        return customers;
    }

    private static void displayArray(Customer[] customers) {
        for (Customer customer : customers) {

            if (customer instanceof PlusCustomer) {
                // why kill your plus customers? ;-)
                customer.addItem(new ItemInfo("Cigarette", 2, 12.96));
            }

            // presentation logic moved into the Customer classes
            // this keeps the Main class focused on the high level
            // process and the Customer classes focused on doing
            // customer logic (cohesion, information hiding)
            // If you find yourself using instanceof always
            // stop and think if you can solve the same issue
            // using polymorphism
            System.out.println(customer);
        }
    }

    public static void main(String[] args) {
        Customer[] customers = constructArray();
        displayArray(customers);
    }
}

The basic Customer class:

public class Customer {

    private final List<ItemInfo> items = new ArrayList<>();
    private final String paymentMode;

    public Customer(String paymentMode) {
        this.paymentMode = paymentMode;
    }

    public void addItem(ItemInfo item) {
        items.add(item);
    }

    public double computePayment() {
        double total = 0.0;
        for (ItemInfo item : items) {
            total += item.getTotal();
        }
        return total;
    }

    public int getNumber() {
        return items.size();
    }

    public String getPaymentmode() {
        return paymentMode;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append(String.format("Mode of payment:%s%n", getPaymentmode()));
        sb.append(String.format("Invoice no:%04d%n%n", getNumber()));
        String itemFormat = "%-20s %-10s %-10s %-10s %n";
        sb.append(String.format(itemFormat, "Item", "Quantity", "Price", "Sub Total"));
        sb.append(String.format("%n"));
        for (ItemInfo item : items) {
            sb.append(String.format(itemFormat, item.getName(), item.getAmount(), item.getPrice(), item.getTotal()));
        }
        sb.append(String.format("%n"));
        sb.append(customPresentation());
        sb.append(String.format("Total payment: %.2f%n", computePayment()));
        return sb.toString();
    }

    /**
     * Sub classes can override this method to provide their own custom part of the presentation logic
     *
     * @return
     */
    String customPresentation() {
        return "";
    }
}

The PlusCustomer has to call the super-constructor to pass the payment method. It only needs to implement the adult field.

public class PlusCustomer extends Customer {

    private final boolean adult;

    public PlusCustomer(String paymentMode, boolean adult) {
        super(paymentMode);
        this.adult = adult;
    }

    public boolean getAdult() {
        return adult;
    }

    @Override
    String customPresentation() {
        return String.format("Customer: %s%n", getAdult() ? "adult" : "minor");
    }
}

ItemInfo calculates it's own total. BTW usually you should avoid using double for such calculations, it's very imprecise in Java. Better to use BigDecimal.

public class ItemInfo {

    private final String name;
    private final int amount;
    private final double price;

    public ItemInfo(String name, int amount, double price) {
        this.name = name;
        this.amount = amount;
        this.price = price;
    }


    public String getName() {
        return name;
    }

    public int getAmount() {
        return amount;
    }

    public double getPrice() {
        return price;
    }

    public double getTotal() {
        return price * amount;
    }
}

Output:

Mode of payment:cheque
Invoice no:0002

Item                 Quantity   Price      Sub Total  

Mutton               1          19.85      19.85      
Stationery           2          17.66      35.32      

Total payment: 55.17
---------------------------------------

Mode of payment:cash
Invoice no:0003

Item                 Quantity   Price      Sub Total  

Fishball             1          12.07      12.07      
T Bone Steak         3          19.2       57.599999999999994 
Stationery           2          12.25      24.5       

Total payment: 94.17
---------------------------------------

Mode of payment:Paynow
Invoice no:0002

Item                 Quantity   Price      Sub Total  

Crab                 2          14.08      28.16      
Cigarette            2          12.96      25.92      

Total payment: 54.08
Customer: adult
---------------------------------------
Adriaan Koster
  • 15,870
  • 5
  • 45
  • 60