So I have 3 classes:
Item
GroupItem extends Item
ProductItem extends Item
I am passing a array of Item objects to a class and want to do separate things depending on their class type.
Would using instanceof be an acceptable way of doing this or should I have an internal boolean isGroup() method which will be set on initialisation of the specified sub-class.
class Item {
protected boolean isGroup = false;
public boolean isGroupItem() { return isGroup; }
}
class GroupItem extends Item {
public GroupItem() {
isGroup = true;
}
}
class ProductItem extends Item {
public ProductItem() {
isGroup = false;
}
}
class Promotion {
// Item can be either a group or a list of items
private List<Item> items = new LinkedList<Item>;
public void addItem(Item itemObj) {
items.addItem(itemObj);
}
public List<Item> getItems() {
return items;
}
}
class Checker {
// Items retrieved from Promotion and passed from another class as array
public Checker(Item[] items) {
// either
if(items[0] instanceof GroupItem) {
// Do something ...
}
// or
if(items[0].isGroupItem()) {
// Do something ...
}
}
}
So my questions are:
instanceof or method?
if method, in Item or Promotion?
and, Why? (Just so i can better understand the reasoning)
Thanks in advance
Use different methods on the actual type you want to pass as parameter.
Usually, using instance of means that there is something wrong in your model.
If you need to have different behaviors for different subtypes, it means that you are not really using the parent type. In addition, you are compelled to know the implementation details, which should not be the case.
If the inheritance you have is only a technical one, try to use composition instead of inheritance.
This is the exact place where you should use instanceOf operator .
The instanceof operator compares an object to a specified type. You
can use it to test if an object is an instance of a class, an instance
of a subclass, or an instance of a class that implements a particular
interface.
There is no point to skatch a new method or some boolean property to do this stuff. You can easily identify specific Object of GroupItem by check instanceOf.
you can also use GroupItem.class.isInstance(items[0]) to check the same. like -
if(GroupItem.class.isInstance(items[0])) {
// Do something ...
}
I guess I would try to define why I need to know if it is a group at this point.
Let's say it is to decide if one Item is eligible for a promotion and the promotion rules might change: then I would use instanceof, because you don't want the promotion rules logic to "pollute" your basic objects.
If being a group is an important property of your item and is useful in various contexts (not just the promotion rules), I would include it at the Item level.
The instaceOf operator is a better choice but I would also consider to use the Visitor pattern.
interface Item{
void accept(CheckerVisitor checker);
}
class GroupItem implements Item{
void accept(CheckerVisitor checker){
checker.visit(this);
}
}
class OtherItem implements Item{
void accept(CheckerVisitor checker){
checker.visit(this);
}
}
class CheckerVisitor{
void visit(GroupItem groupItem){
//do specific things to GroupItem
}
void visit(OtherItem otherItem){}
}
class MyClassOfItems{
List<Item> items = ...;
for(Item item : items){
item.accept(new CheckerVisitor());
}
}
So after reading this i chose a different path for my solution. Thanks for everyone who helped.
The solution I chose allows me to not even be bothered with what sub-type the object is (thanks to Assylias and Balázs Mária Németh for making me rethink my structure) as I use abstract classes to just get the information i need.
abstract class Item {
public Item(...) {
initialise();
createSQLSegment();
}
protected String SQLSegment = "";
protected abstract void createSQLSegment();
public String getSQLSegment() {
return SQLSegment;
}
...
}
// Concrete class creates response
class GroupItem extends Item {
...
// Concrete method
protected void createSQLStatement() {
SQLStatement = "...SQL...";
}
}
class ProductItem extends Item {
...
// Concrete method
protected void createSQLSegment() {
SQLSegment = "...SQL..."
}
}
class Promotion {
// Item can be either a group or a list of items? extends Item>;
public void addItem(Item itemObj) {
items.addItem(itemObj);
}
public List<Item> getItems() {
return items;
}
}
class Checker {
// Items retrieved from Promotion and passed from another class as array
public Checker(Item[] items) {
...
for(Item item : Items) {
addPreparedSQLToBatch(item);
}
}
private void addPreparedItemToBatch(Item item) {
...
// No need to know concrete class
SQLString += Item.getSQLSegment();
...
}
}
Thanks again to everyone.
Comments welcome, I'm always learning :-)
Related
Item is an abstract class with subclasses Potion, Weapon. Shield.
The useItem() method is an abstract method defined in each of Item's subclasses
get_item return object of class Item
The getItem method returns an object of class subclass of Item
case "use":
if (hero.get_item() instanceof Potion) {
hero.<Potion>getItem(Potion.class).useItem();
} else if (hero.get_item() instanceof Weapon) {
hero.<Weapon>getItem(Weapon.class).useItem();
} else if (hero.get_item() instanceof Shield) {
hero.<Shield>getItem(Shield.class).useItem();
}
break;
is there a way I can condense this code into something like...
Class itemclass = hero.getItem().getClass();
hero.<itemclass>getItem(itemclass.class).useItem();
The code above does not compile but I am looking for something like it. I am trying to avoid if else statements because there are many more items.
Edit:
The reason i did not initially use hero.get_item().useItem() was because
i was trying to do
Weapon sword = hero.get_item();
so i could access methods such as sword.getDamage()
However, I would get the error error: incompatible types: Item cannot be converted to a Weapon
so that is why I created (help from #marsouf) hero.<Weapon>getItem(Weapon.class)
Today i created the method abstract public void useItem();
and since it is a method of the Item class I am able to use hero.getItem().useItem()
It would make more sense to haven an Interface for Item with the method useItem().
Then have an implementation for Potion, Shield etc.
This way you avoid having to cast and make it more complex than it is.
useItem() does not belong in the abstract class if its not giving any functionality, and less needed now Interfaces can have default methods.
My idea is to use the magic of generics without not cast
public class Character<T extends Item> {
private T item;
public Character (T item){
this.item = item;
}
public T getItem(){
return item;
}
}
When you create a hero:
Character hero = new Character<Weapon>(new Weapon("sword"));
after this you can use it like:
hero.getItem().useItem(); // abstract method from Item class
hero.getItem().getPower(); //where power is a Weapon method
Character class you can extend like:
public class Hero<T> extend Character<T>{
//add there your custom methods or override Character methods
}
Difficult to answer without seeing the contracts being involved (hero.get_item(), hero.getItem()).
But have you tried:
Class<?> itemClass = hero.get_item().getClass();
hero.getItem(itemClass).useIt();
?
Assuming you are set on using generics the way you're using them... here's how.
First, I've created some extremely simple classes to mimic your structure from this and your other question: a class which uses instances of a particular abstract class.
public class ACOne extends AbstractClass
{
#Override
public void use(){System.out.println("Used item ACOne!");}
}
public class ACTwo extends AbstractClass
{
#Override
public void use(){System.out.println("Used item ACTwo!");}
}
public abstract class AbstractClass
{
public abstract void use();
}
public class UserClass
{
private AbstractClass item;
public UserClass (AbstractClass item)
{
this.item = item;
}
public Class<? extends AbstractClass> getItemClass()
{
return item.getClass();
}
public <T extends AbstractClass> T getItem (Class <? extends T> targetType)
{
return targetType.cast(this.item);
}
public void setItem (AbstractClass item)
{
this.item = item;
}
}
public class CastingSubclasses
{
public void testCastingSubclasses()
{
UserClass user = new UserClass(new ACOne());
user.setItem(new ACTwo());
user.getItem(user.getItemClass()).use();
}
}
This program, when run, prints out "Used item ACTwo!"
The crux here is in the getItemClass method on the UserClass (your Character class).
Also, it's common to call these methods which get the Class object 'getClazz', since there is a default method 'getClass' that you don't want to override.
Here it made sense to just keep the spelling.
I've interface MyInterface and two classes ( Summary, Detail) are implementing this, and overriding compareTo.
A third class DetailMe is extending Detail
Another class (MyApi) is using List<MyInterface> , and complaining for Collections.sort , what am i doing wrong ?
public class Summmary implements MyInterface, Comparable<Summary> {
private Detail detail;
public Summary(Detail detail) {
this.detail = detail;
}
// properties
// methods
#Override
public int compareTo(Summary o) {
// Do work
}
}
==
public class Detail implements MyInterface, Comparable<Detail> {
// properties
// methods
#Override
public int compareTo(Detail o) {
// Do Detail work
}
}
==
public class DetailMe extends Detail {
// Do work
}
==
public class MyApi {
private List<MyInterface> myList;
public MyApi(List<MyInterface> myList) {
this.myList = myList
Collections.sort(this.myList); // COMPLIATION ERROR
}
}
You need to ensure (in a way the compiler knows of) that the list only contains one type of object, because your objects can't compare with each other.
So, your list must be either a List<Summmary> or a List<Detail>.
That is, unless you define the ordering of mixed elements, but then both classes would have to implement public int compareTo(MyInterface o).
i have one class implemeting two different interfaces ,example:
public interface SortList{
void Sort();
search();
delete();
}
public interface unSortedList{
search();
delete();
}
class List implements ordenedList,unordenedList{}
I wanted to switch between ordened interface or not ordened list,thank you for your help.
You have to make 2 different class like OrderedList and UnorderedList. Implements are just a guarantee you have implemented those mehtots. So you have to make a switch case or if else in your methods or you have to make two different class.
I don't know what meaning you put behind "Ordened" and "UnOrdened", and if it makes sens to have a class implementing both, but anyway, here is an example on how you can use multiple implementations of interfaces, trying to keep up with your stuff.
I suppose by ordered the Java meaning as explained here, which is not sorted.
Let's say an Ordered interface gives the possibility to access some object (of type T) by index and to search or find for a given object:
public interface Ordered<T> {
T getNth(int i);
T find(T o);
}
Let's say an UnOrdered interface provides only the way to find an object:
public interface UnOrdered<T> {
T find(T o);
}
Now we can defined a class that implements both interfaces:
public class MyList<T> implements Ordered<T>, UnOrdered<T> {
List<T> theList;
public MyList(T...a) {
theList = Arrays.asList(a);
}
// The list of objects are neither sorted nor hashed.
// Only way to find an object is to iterate through the list
public T find(T o) {
for (T e : theList) {
if (e.equals(o)) {
return e;
}
}
return null;
}
public T getNth(int i) {
return theList.get(i);
}
}
public static void main(String[] args) {
MyList<Integer> mylist = new MyList<>(2,8,6,1,7,3,5,9,10,4);
System.out.println(mylist.getNth(3));
// => print 1
System.out.println(mylist.find(3));
// => print 3
System.out.println(mylist.find(42));
// => print null
}
You could have done a better job in describing the problem and what you are trying to solve for, assuming that I understood the problem, you can achieve this with Java 8 Default methods here is how
public interface UrdenedList{
void ordenate();
default T search(){
// default implementation here
}
default void delete(){
// default implementation here
}
}
public interface UnordenedList{
default T search(){
// default implementation here
}
default void delete(){
// default implementation here
}
}
I wanted to switch between ordened interface or not ordened list,thank
you for your help.
class List implements OrdenedList,UnordenedList{
void ordenate(){
// implementation here
}
T search(){
if(condition)
return OrdenedList.search();
else
return UnordenedList.search();
}
}
void delete(){
if(condition)
OrdenedList.delete();
else
UnordenedList.delete();
}
}
Notice Resolving conflicts by Explicitly choosing to call methods from interfaces OrdenedList, and OrdenedList
I'm having a bit of a problem grasping something - I might be going about this completely wrong.
I am trying to create a class which extends ArrayList but has several methods which increase the functionality (at least for the program I am developing.)
One of the methods is a findById(int id), which searches each ArrayList object for a particular id match. So far it's working, but it won't let me do for (Item i : this) { i.getId(); }
I don't understand why?
Full code:
public class CustomArrayList<Item> extends ArrayList<Item> {
// declare singleton instance
protected static CustomArrayList instance;
// private constructor
private CustomArrayList(){
// do nothing
}
// get instance of class - singleton
public static CustomArrayList getInstance(){
if (instance == null){
instance = new CustomArrayList();
}
return instance;
}
public Item findById(int id){
Item item = null;
for (Item i : this) {
if (i.getId() == id) {
// something
}
}
return item;
}
public void printList(){
String print = "";
for (Item i : this) {
print += i.toString() + "\n";
}
System.out.println(print);
}
}
Change
public class CustomArrayList<Item> extends ArrayList<Item> {
to
public class CustomArrayList extends ArrayList<Item> {
I suspect Item is the name of the class that you want to store in the list. By adding <Item> after CustomArrayList you're introducing a type parameter which shadows this class.
With the <Item> parameter, your code is equal to
public class CustomArrayList<T> extends ArrayList<T> {
// ...
for (T i : this) { i.getId(); }
// ...
}
which obviously won't always work, as T may refer to any type.
What is getId()? Presumably it's a method in some class, but we don't know which class.
If you've actually got a class called Item with a getId() method, which this is meant to be a list of, you simply need to stop your class from being generic. So instead of this:
public class CustomArrayList<Item> extends ArrayList<Item> {
you want:
public class CustomArrayList extends ArrayList<Item> {
Currently within your class, Item doesn't refer to a class called Item, it refers to a type parameter called Item.
Now personally:
I wouldn't avoid creating singletons unless you really have to
If you have to, I'd avoid creating them in the way you have (which isn't thread-safe)
I wouldn't extend ArrayList<> unless I really had to, preferring composition over inheritance
I have a base Entity class:
public class Entity
{
abstract int getTypeID();
}
The method int getTypeID() returns a number that is unique to that class:
public class OneEntity extends Entity
{
int getTypeID()
{
return 1; // Actually defined in a constants class
}
}
Now I want to be able to safely cast it and assign it.
I can do it this way:
public void castTheEntityAndDoSomething(Entity ent)
{
if (isType(ent, 1)) // this is the 1 from the OneEntity class
{
OneEntity oneEnt = (OneEntity)ent;
// ... and then do something
}
}
public bool isType(Entity ent, int type)
{
return ent.getTypeID() == type;
}
But what I'd like to do is combine the cast and the type check in one line.
Something like:
if (OneEntity oneEnt = entityCast(ent, 1))
{
// use a method specific to the OneEntity class
}
Is this possible? What does the method look like?
It would be even better if I could use the subclass name as an argument of the safe casting method.
EDIT:
So I've written this method to avoid the type id etc. etc.
#SuppressWarnings("unchecked")
public static <T extends Entity> T castEntity(Entity ent)
{
if (ent instanceof T)
{
return (T)ent;
}
return null;
}
But it has this error:
Cannot perform instanceof check against type parameter T. Use instead its erasure Entity since generic type information will be erased at runtime
What do I need to do to fix this compile error? Is this even going in the right direction?
In case you have defined this typeId for the sole purpose of casting, then please don't do that and use instanceof operator instead.
Then you just need to do something like this,
if (ent instanceof EntityOne) {
OneEntity oneEntity = (OneEntity) ent;
// do whatever
}
Now, taking your example here,
if (Entity oneEnt = entityCast(ent, 1))
{
// do something with oneEnt
}
Suppose this if statement of yours work and your entityCast() method return OneEntity. Don't you think you are again getting that returned value in type Entity. Hence spoiling the whole purpose.
So, please explain what are you trying to do exactly.
If you implement an interface which defines the methods you want to call on them from the "do something" blocks and implement that inteface in all your derived entiries, your have solved problem by using polymorphism.
You might want to rethink your architecture.
First of all maybe you should not have a typeID field in the class.
You should rely on the "power" of inheritance and/or interfaces instead.
// Entity.java
public interface Entity { abstract public void doSomeThing(); }
// EntityType1.java
public class OneEntity extends Entity {
#Override
public void doSomeThing() {
// do something specific for entity type 1
}
}
// EntityType2.java
public class TwoEntity extends Entity {
#Override
public void doSomeThing() {
// do something specific for entity type 2
}
}
// Main.java
public static void main(String[] args) {
Entity e1 = new EntityType1();
Entity e2 = new EntityType2();
e1.doSomeThing();
e2.doSomeThing();
}
You should encapsulate the different behavior in the implementation classes.
If you think you have good reasons to detach the behavior from the entities you would better use this same logic in a hierarchy of external processing classes (maybe with a simple factory on top) and use "instanceof" in the factory method.
I suppose that you could do this:
OneEntity oneEnt;
if (isType(ent, 1) && (oneEnt = (OneEntity) ent) != null) {
// ... and then do something
}
... or the equivalent with instanceof instead of your icky isType method ...
Or you could define a method called oneEntityOrNull as follows:
public OneEntity oneEntityOrNull(Entity ent) {
return ent instanceof OneEntity ? (OneEntity) ent : null;
}
and then
OneEntity oneEnt;
if ((oneEnt = oneEntityOrNull(ent)) != null) {
// ... and then do something
}
but IMO both of these are a BAD IDEA since the code is harder to understand than the original.