-1

I need help with creating subclasses, the subclass doesn't override the parents method. I use a Registry object to collect all the items.

public class Registry {
private final Item[] items = new Item[100];
private int pos =0;

public Item[] getItems(){

    return items;
}

public void addItem(Item item) {

    items[pos] = item;
    pos ++;
}
public void addItem(Item item, int nrTimes) {

    for(int i = 0; i<nrTimes;i++){
        items[pos] = item;
        pos++;
    }
}
public double totPrice(){

    double total = 0.0;
    for(int i = 0; i < items.length; i ++){
        if(items[i] == null) break;
        total += items[i].getNr(this);

    }
    return total;
}

public String toString() {
    String result = "";

    for(int i= 0; i < items.length;i++){
        if (items[i] == null) break;
           result = result + items[i].getName()+" $" + items[i].getNr(this) + " Sold by " + items[i].getName2()+ "\n";

    }
    return result;
}

}

public class Item {
private final String name;
private final String name2;
private final double nr1;

public Item (String name, String name2, double nr1) {
    this.name = name; this.name2 = name2; this.nr1 = nr1;
}

public Item (Item obj) {
    name = obj.name; name2 = obj.name2; nr1 = obj.nr1;
}

public double getNr(Registry reg) { return nr1;}

public final String getName() { return name;}

public final String getName2() { return name2;}

}

The subclass

public class ReducedItem extends Item {
private final Item obj = this;
private final double reduce; // in %

public ReducedItem (Item obj, double reduce) {
    super(obj);
    this.reduce = reduce/100;
}

public double getNr(Registry reg){
    double nr;
    nr = super.getNr(reg) - (super.getNr(reg)*reduce);
    return nr;
}

}

here is the main where I create the objects

     import java.util.*;
     public class Main {
      static Scanner scan = new Scanner(System.in);
      public static void registerItems(Registry reg){
       String name,seller;
       double price;
       int nrprod,discount;
       scan.nextLine();
       System.out.print("Product name: ");
       name = scan.nextLine();

       System.out.print("Seller: ");

       seller = scan.nextLine();

       System.out.print("Price: ");
       price = scan.nextDouble();
       System.out.print("How many: ");
       nrprod = scan.nextInt();
       System.out.print("Discount (enter 0 if no discount applies): ");
       discount = scan.nextInt();

       Item item = new Item(name,seller,price);
       if(discount >0) {
          ReducedItem redItem = new ReducedItem(item, discount);
          if(nrprod == 1){
             reg.addItem(redItem); 
         }else{
             reg.addItem(redItem, nrprod);
         }

     }else{
         if(nrprod == 1){
             reg.addItem(item);
         }else{
             reg.addItem(item, nrprod);
         }
     }

 }

public static void main(String[] args) {
    System.out.println("Welcome");
    System.out.println("What's your name?");
     String customer = scan.nextLine();
     System.out.println("Hi "+customer+". Please choose one of the following options:");
     System.out.print("buy product (1) or checkout (0)");


     Registry reg = new Registry();
     int input = scan.nextInt();
     while(input == 1){
         registerItems(reg);
         System.out.println("buy product (1) or checkout (0)");
         input = scan.nextInt();
     }
     System.out.println(reg.toString());
     System.out.println("Your total is " + reg.totPrice());

}

}

input in the registerItems

    Product name = Car
    Seller = Jack
    Price = 10000
    how many =1
    discount = 20
    checkout = 0

output

    Car $8000 Sold by jack

    Your total is 8000.0

The car is now discounted by 20%

J_crow
  • 1
  • 4
  • 1
    Please post full code. Might try adding the `@Override` annotation for the `getNr()` method in the subclass. Let me know if this is what you were missing and I'll move it to an answer post. Also, you should avoid naming your classes `object` to avoid confusion with Java's built-in `Object` class. – code_dredd Oct 14 '15 at 21:25
  • Also, you should use CamelCase when naming Java objects. – pushkin Oct 14 '15 at 21:29
  • "`Not really know how to ask this [...]`" - then _please don't_ ask. I voted to close this question with "unclear what you're asking". Please edit your question and add what you expect, what you get instead and what you've tried so far solving the problem. Please make your question as general as possible - think how you'd like to read another question about some problem. – try-catch-finally Oct 14 '15 at 21:34
  • How do you _know_ which method is called? – Solomon Slow Oct 14 '15 at 21:52
  • When I look at the subclass method in Eclipse it does say: Overrides getNr() in Item – J_crow Oct 14 '15 at 21:53
  • That doesn't tell you which method actually is called at run-time. Have you run it in a debugger? Have you tried adding `println()` statements? The `ReducedItem.getNr()` method from your example doesn't compile (_error: variable nr might not have been initialized_), so either your example is not the code that you tried to run, or else you haven't even tried to run it. – Solomon Slow Oct 14 '15 at 21:56

3 Answers3

2

Bug Explained

Your bug is in the following set of lines within your Registry class. (I got the source code you had posted before.)

public void addItem(Item item) {
    items[pos] = new Item(item);
    pos ++;
}

You create an entirely new Item for no reason, even when the client wants to add a ReducedItem, so you're effectively removing the discount by replacing ReducedItems for Items and causing ReducedItem.getNr overload to never be used. You should be able to see that if you run the code step-by-step in the debugger.

This line should simply be items[pos++] = item;.

Also note that your Registry class has several of problems and hidden bugs. For example,

  1. this bug is also present in the unnecessary addItem(Item, int) method overload;
  2. the badly named getNr method expects an instance of the Registry to then not use it for anything;
  3. add enough items to your registry and you're going to hit an index out of range exception when pos is larger than items.length;
  4. the Registry is very inefficient and is wasting a lot of space (e.g. new Item[100]) and then looping around all 100 of them every time, even if few or no items were added.

There're several other problems I won't go into, so I'd recommend you compare your code with my recommendations below and understand why they're different.


Code Redesigned

The code you provided has several compilation errors and was confusing to work with. Instead, I tried to understand what you were trying to do and have refactored the code to, hopefully, clarify some things and, at the same time, show you some things you should consider to improve your code and simplify it.

Understand that you'll need to adapt what I've written here.

Summary

I've created an Example class to represent the application. It uses the an ItemRegistry to register Items. The Item interface is implemented by a RegularItem (i.e. an Item with no discount) and an ItemDiscount, which is one example of the Decorator Design Pattern.

Item Interface:

public interface Item {
    String getName();
    double getCost();
}

This should be straightforward: basically a type has been declared. Generally, you should have a preference for using interfaces to specify new types as it allows the rest of the application to rely on a contractual agreement (i.e. the spec) rather than on an implementation detail (i.e. the class). This is what interfaces are for.

WARNING: You should really NOT use floating point types to handle moneye in real-world applications. Basically it's due to the accumulation of rounding errors that will slowly cause your results to be incorrect. See this post for more information. I use it here because this is a simple toy example.

RegularItem Class:

public class RegularItem implements Item {
    private String name;
    private double cost;

    public RegularItem(String name, double cost) {
        this.name = name;
        this.cost = cost;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public double getCost() {
        return cost;
    }
}

This should be self-explanatory: a simple implementation of an item with a name and its associated cost. A regular item has no discount applied. Things start to get more interesting with the ItemDiscount class.

ItemDiscount Class (Decorator)

// Decorator class
public class ItemDiscount implements Item {
    private Item item;
    private double discount;

    public ItemDiscount(Item it, double amount) {
        item = it;
        discount = amount / 100.0; // e.g. 15/100 = 15%
    }

    @Override
    public String getName() {
        return item.getName();
    }

    @Override
    public double getCost() {
        return item.getCost() - (item.getCost() * discount);
    }
}

It basically wraps whatever Item needs to have a discount applied and returns the discounted cost when the client requests the cost of the Item. This has several advantages:

  1. The client only sees Items and is not aware which ones have or don't have discounts. It doesn't need to be.
  2. You can apply as many discounts as you want on the same item (e.g. customer with 5 discount coupons? no problem!)
  3. Your application is easier to understand and maintain

There's more, but I won't go there now. Let's follow up with the ItemRegistry below:

ItemRegistry Class

import java.util.ArrayList;
import java.util.List;

public class ItemRegistry {
    private List<Item> items = new ArrayList<Item>();

    public ItemRegistry() {}

    void registerItem(Item i) {
        items.add(i);
    }

    double getTotalCost() {
        double total = 0.0;
        for(Item i : items)
            total += i.getCost();
        return total;
    }

    List<Item> getItems() {
        return items;
    }
}

Note several things in this simplified version.

  1. The class doesn't need to handle "special cases" for discounts or non-discounts. (Remember the customer with 5 coupons? No changes needed here!)
  2. The class communicates using interfaces rather than classes, which are implementation details that should remain encapsulated.

Note that the registry doesn't need to know about the specific types of items it has --only that they're items. (You don't want to have to update the entire application every time a new kind of item is defined, or worse, force those who will need to maintain your code to do that, do you?)

The client using the registry also does not need to know whether it's using an ArrayList or something else --only that it's a List. This makes it more space efficient and fixes the bugs your code will hit after adding 100+ items.

You can see this with the Example class below as it uses the ItemRegistry:

Example Class (Application)

public class Example {
    public static void main(String[] args) {
        ItemRegistry reg = new ItemRegistry();
        reg.registerItem(new RegularItem("Toy Gun", 10));
        reg.registerItem(new ItemDiscount(new RegularItem("Toy Car", 100), 10));  // apply 10% discount to Toy Car item

        for(Item item : reg.getItems()) {
            System.out.println("Name: " + item.getName());
            System.out.println("Cost: " + item.getCost());
        }
        System.out.println("Total: " + reg.getTotalCost());
    }
}

This example should make it easier for you to follow the code step-by-step in the debugger so that you can see how the methods are being called and how the overrides are actually working.

Program Output:

Name: Toy Gun
Cost: 10.0
Name: Toy Car
Cost: 90.0
Total: 100.0

And is correct :)

Community
  • 1
  • 1
code_dredd
  • 5,915
  • 1
  • 25
  • 53
  • very nice! I've fixed the compilation errors on my code, most where because I didn't add my on I/O class but now I use sysout for output and java.util.Scanner for the input. Your code seems to work, but I would like to know whats wrong with mine – J_crow Oct 15 '15 at 12:35
  • @J_crow: I still don't see source code for your `Registry` class --and I see `ReducedItem` listed twice. – code_dredd Oct 15 '15 at 17:52
  • @J_crow: I'll add that something wrong with your code is that it's difficult even for you to debug --and you wrote it. Simplify, choose better names for your variables and methods, etc. – code_dredd Oct 15 '15 at 18:13
  • @J_crow: I have updated the post with an explanation of your bug. May your frustration trying to figure out your own code serve as a catalyst to lead you down path to better code :) – code_dredd Oct 15 '15 at 18:30
  • I didn't even realize about the Registry, must have accidentally removed it when editing. I have made the code look much nicer now. This was an exercise assignment so I had restrictions. Fixed better names aswell – J_crow Oct 15 '15 at 21:26
0

Here is a working example of what I imagine you are looking for.

public class J_crow {
  public static void main(String... args) {
    Item item = new Item("Name One", "Second Name", 42);
    System.out.println(item);
    Item reducedItem = new ReducedItem(item, 20);
    System.out.println(reducedItem);
  }
}

class Item {
  private final String name;
  private final String name2;
  private final double nr1;

  public Item(String name, String name2, double nr1) {
    this.name = name;
    this.name2 = name2;
    this.nr1 = nr1;
  }

  public Item(Item obj) {
    this(obj.getName(), obj.getName2(), obj.getNr());
  }

  public double getNr() {
    return nr1;
  }

  @Override
  public String toString() {
    return String.format("I am an [%s], I cost [%f]", getClass().getCanonicalName(), getNr());
  }

  public String getName2() {
    return name2;
  }

  public String getName() {
    return name;
  }
}

class ReducedItem extends Item {
  private final double reduce; // in %

  public ReducedItem(Item obj, double reduce) {
    super(obj);
    this.reduce = reduce;
  }

  @Override
  public double getNr() {
    return super.getNr() * (1 - (reduce / 100d));
  }
}

Output:

 I am an [Item], I cost [42.000000]
 I am an [ReducedItem], I cost [33.600000]
brimborium
  • 9,362
  • 9
  • 48
  • 76
Andreas
  • 4,937
  • 2
  • 25
  • 35
  • This works, it seems my problem lies somewhere else though, I am going to update to show the entire code. I'm sorry for this, I was sure the problem was in the code i posted. – J_crow Oct 14 '15 at 22:26
0

I'm so stupid! I figured out my problem

public void addItem(Item item) {

    items[pos] = new Item(item);
    pos ++;
}
public void addItem(Item item, int nrTimes) {

    for(int i = 0; i<nrTimes;i++){
        items[pos] = new Item(item);
        pos++;
    }
}

I create a new Item every time, I need to use the passed argument instead

public void addItem(Item item) {

    items[pos] = item;
    pos ++;
}
public void addItem(Item item, int nrTimes) {

    for(int i = 0; i<nrTimes;i++){
        items[pos] = item;
        pos++;
    }
} 

Thank you, I learned many things reading your answers and comments

J_crow
  • 1
  • 4
  • Hi J_Crow, "thank your"s around here are generally provided by up-voting responses and accepting the answers you found most helpful. – code_dredd Oct 15 '15 at 18:51