I'm writing a program that keeps track of how many assets a person has. Each person class has an ArrayList of what is called a Value_Item object.
Value_Item is a simple object.
class Value_Item
{ float _value;
String _currency;
String _type //+getters and setters}
I assume the code above is self-explanatory if not happy to clarify anything.
From Value_Item I extend to a few classes, such as Equity, Fund, Cash etc. All of these have their various properties etc, such as Equity "unit price".
My current solution to storing the information is to create a subclass and when I call the superclass constructor set the type of the subclass then when I need to act on the data, say get the unit price of all held funds, I check the type and downcast.
Eg
if(_value_item.getType()=="FUND){
Fund fund = (Fund) _value_item
float unit_price = fund.getUnitPrice()}
I have however read that downcasting is not favoured, and the solution itself doesn't feel very elegant, is there a better way of doing this java's equivalent of more "pythonic".
I have however read that downcasting is not favoured
Indeed. It should be used only as last resort. Essentially because checking the types of the objects to perform a processing is not maintainable and it also produces a less safe code at compile time as the check type will only occur at runtime.
is there a better way of doing this java's equivalent of more "pythonic".
In your case the main issue is that Value_Item
represents a too wide concept, it is like if you defined a class ValueForAnyThing
.
So hard to use elements of List<ValueForAnyThing>
without downcast.
Subclassing even if it is not its main role may be helpful to prevent duplicate code but subclassing may also appear helpless as you want to use the objects of the subclasses in an uniform way but that the base class is too broad.
In case of collections/arrays that declare as element type a common base type, two known ways to prevent downcasting are :
1) Moving the behavior in a base class method.
2) Not gathering all subclasses instances in a same collection/array.
1) Moving the behavior in a base class method.
Suppose that the following extract makes part of a computation that all subclasses do but that each one with its own fields/methods :
if(_value_item.getType()=="FUND){
Fund fund = (Fund) _value_item
float unit_price = fund.getUnitPrice()}
}
You could introduce an abstract compute()
method in ValueItem
:
public class ValueItem{
public abstract float compute();
// ...
}
And subclasses will implement it :
public class Cash extends ValueItem{
@Override
public float compute(){
// ...
}
// ...
}
public class Equity extends ValueItem{
@Override
public float compute(){
// ...
}
// ...
}
Now you can invoke the compute()
method on any elements (or all) of the List
:
for (ValueItem valueItem : valueItems){
float computation = valueItem.compute();
// ....
}
2) Not gathering all subclasses instances in a same collection/array.
You could indeed split the List
into multiple parts to group together things that should be manipulated uniformly.
Note also that if multiple kinds of items could be manipulated uniformly you could introduce an interface common to them.
For example, suppose that Equity
and Fund
could be manipulated uniformly because they belong to a same specific concept : UnitPricable
and suppose Cash
is a specific concept that has its own way to be represented/manipulated, you could so get something like :
public class Cash extends ValueItem{
...
}
public class Equity extends ValueItem implements UnitPricable{
...
}
public class Fund extends ValueItem implements UnitPricable{
...
}
public class Person{
private List<Cash> cashes = new ArrayList<>();
// the next one groups two kind of subclasses
private List<UnitPricable> unitPricables = new ArrayList<>();
....
// operations that works on the Cash subclass instances
public void computeCashes(){
for (Cash cash : cashes){
... = cash.getSpecificCashField();
... = cash.specificCashMethod();
...
}
}
// operations that work on multiple subclasses instances : Fund and Entity
public void computeUnitPricables(){
for (UnitPricable up: unitPricables){
... = up.getUnitPrice();
... = up.specificUnitPriceMethod();
...
}
}
}