0

In the Java class below two search methods are defined. They are differentiated based on searchSpec type. My understanding is, when the search method is called from somewhere, it is checked what is the type of searchSpec and depending on that type the correct search method is invoked.

Now, I have implemented this class in Python using abstract base classes. Both GuitarSpec and MandolinSpec are inheriting from my abstract base class, and have their own matches method. But I don't have the possibility to create two search methods in the Inventory because the type of the arguments are irrelevant in Python.

That means in Python when the search method of Inventory is invoked, there is a possibility that a list containing both Guitar and Mandolin objects would return. So, I like to know how can I get the same advantage of Java type checking of inputs somehow implemented in Python, so that I can ensure the search method returns only instruments of the same class type.

This is the Java class

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

public class Inventory {

  private List inventory;

  public Inventory() {
    inventory = new LinkedList();
  }

  public void addInstrument(String serialNumber, double price,
                            InstrumentSpec spec) {
    Instrument instrument = null;
    if (spec instanceof GuitarSpec) {
      instrument = new Guitar(serialNumber, price, (GuitarSpec)spec);
    } else if (spec instanceof MandolinSpec) {
      instrument = new Mandolin(serialNumber, price, (MandolinSpec)spec);
    }
    inventory.add(instrument);
  }

  public Instrument get(String serialNumber) {
    for (Iterator i = inventory.iterator(); i.hasNext(); ) {
      Instrument instrument = (Instrument)i.next();
      if (instrument.getSerialNumber().equals(serialNumber)) {
        return instrument;
      }
    }
    return null;
  }

  public List search(GuitarSpec searchSpec) {
    List matchingGuitars = new LinkedList();
    for (Iterator i = inventory.iterator(); i.hasNext(); ) {
      Guitar guitar = (Guitar)i.next();
      if (guitar.getSpec().matches(searchSpec))
        matchingGuitars.add(guitar);
    }
    return matchingGuitars;
  }

  public List search(MandolinSpec searchSpec) {
    List matchingMandolins = new LinkedList();
    for (Iterator i = inventory.iterator(); i.hasNext(); ) {
      Mandolin mandolin = (Mandolin)i.next();
      if (mandolin.getSpec().matches(searchSpec)) 
        matchingMandolins.add(mandolin);
    }
    return matchingMandolins;
  }
}

This is the Python class

class Inventory:
    def __init__(self):
        self.instruments: List[Union[Guitar, Mandolin]] = []

    def add_instrument(self, serial_number: str, price: float, spec: Union[GuitarSpec, MandolinSpec]):
        if isinstance(spec, GuitarSpec):
            instrument = Guitar(serial_number, price, spec)
        else:
            instrument = Mandolin(serial_number, price, spec)
        self.instruments.append(instrument)

    def get(self, serial_number) -> Union[Guitar, Mandolin, None]:
        for instrument in self.instruments:
            if instrument.serial_number == serial_number:
                return instrument
        return None

    def search(self, search_spec: Union[GuitarSpec, MandolinSpec]) -> List[Union[Guitar, Mandolin]]:
        matching_instruments = []
        for instrument in self.instruments:
            if instrument.get_spec().matches(search_spec):
                matching_instruments.append(instrument)
        return matching_instruments
Payam Mesgari
  • 953
  • 1
  • 19
  • 38

1 Answers1

0

First things first: In the Java code, you are using raw types and not proper type-checked generics. For instance,

 private List inventory;

should be something like

private List<? extends Instrument> inventory;

public Inventory() {
    inventory = new LinkedList<>();
}

Moreover, you also use raw iterators and end up in doing something like this:

Guitar guitar = (Guitar)i.next();

which just compiles with a warning but will throw you a ClassCastException at runtime. Basically, you have the same problem as with the Python code.

After you changed the raw types, one way to distinguish the instruments is to filter them based on the type and the predicate, e.g. something like this:

inventory.stream().filter(item -> item instanceof Guitar).filter(...)

In Python, you could use list comprehension to filter the elements.

momo
  • 3,313
  • 2
  • 19
  • 37