1

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()

  • 7
    If `useItem()` is defined on `Item`, which is what `get_item()` is returning, why not just `hero.get_item().useItem()`? – azurefrog Aug 03 '17 at 17:35
  • 2
    since all the classes seem to implement the useItem() method you should be able to simply do hero.get_item().useItem(); – RAZ_Muh_Taz Aug 03 '17 at 17:35
  • that does not work. [https://stackoverflow.com/questions/45466740/return-a-subclass-in-a-getter-method-without-knowing-the-subclass/45467766?noredirect=1#comment77900579_45467766] –  Aug 03 '17 at 17:37
  • 2
    @kevinLloyd In your case, you don't need to know the subclass. The correct implementation of `useItem`  will be called by the runtime even if the declared type is just `Item` – litelite Aug 03 '17 at 17:39
  • actually I will try that. I hadn't created the use item function yesterday so I had to create that really strange function –  Aug 03 '17 at 17:39
  • ["Replace Conditional with Polymorphism"](https://stackoverflow.com/questions/30922788/what-is-replace-conditional-with-polymorphism-refactoring-how-is-it-implemented) – chrylis -cautiouslyoptimistic- Aug 03 '17 at 18:01
  • Could you show your `Item` class (or interface)? – Radu Dumbrăveanu Aug 07 '17 at 10:53

4 Answers4

1

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.

UserF40
  • 3,533
  • 2
  • 23
  • 34
1

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
}
Vasile Bors
  • 656
  • 6
  • 14
  • With your idea is it possible to change the item the hero has with a setter method? –  Aug 03 '17 at 18:09
  • https://stackoverflow.com/questions/45466740/return-a-subclass-in-a-getter-method-without-knowing-the-subclass I went with marsouf's answer –  Aug 03 '17 at 18:11
  • You can change item only to be of same type, for my example, the hero can have only another Weapon but can not has an Potion. If you want to can change Weapon with setter but to have different methods, move the logic of using item to Item class/subcalss or to one delagate – Vasile Bors Aug 03 '17 at 18:14
  • @kevinLloyd then you should accept marsouf's answer: https://stackoverflow.com/help/someone-answers – Jeutnarg Aug 03 '17 at 18:17
0

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();

?

albert_nil
  • 1,648
  • 7
  • 9
  • that does not work. It gives illegal start of type error –  Aug 03 '17 at 17:47
  • can you update your question, adding the signatures for get_item() and getItem() ? otherwise we're just guessing what your api is about. – albert_nil Aug 03 '17 at 17:53
  • i had a typo on the itemClass declaration, try it now. but will be much easier if you just provide the signatures of your hero methods. – albert_nil Aug 03 '17 at 18:00
0

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.

Jeutnarg
  • 1,138
  • 1
  • 16
  • 28