Search code examples
javaoopinstanceofdowncast

How to access class specific methods without instanceof or getClass


Suppose I have the following interface and implementation:

interface Weapon{
    int attack();   
}

public class Sword implements Weapon {

    //Constructor, and Weapon interface implementation
    //...
    public void wipeBloodfromSword(){}
}


public class ChargeGun implements Weapon {
    //Constructor, and Weapon interface implementation  
    //...
    public void adjustlasersight(){}
}

and Store them like this:

List<Weapon> weaponInventory = new ArrayList<Weapon>();
weaponInventory.add(new Sword());
weaponInventory.add(new ChargeGun());

Problem:

Given that they're stored in the List<Weapon> I obviously only have access to the methods declared in the Weapon interface. If downcasting and the use of instanceof/getClass() should be avoided, how would I get access the the class specific methods wipeBloodfromSword() and adjustlasersight()?

Possible Solution:

Given that there are actions before and after the attack method is called, I can re-write my interface like this:

interface Weapon{

   //Can reload a weapon, adjust a laser sight 
   //do anything to the weapon to prepare for an attack
   void prepareWeapon(); 
   int attack();
   //Not sure of a more proper name, 
   //but you can wipe blood off sword  or take off silencer 
   void postAttackActions();
}

While, I'm in control of this hobby project, I might run into a situation where I'm unable to change the interface, while the interface re-write may solve this specific problem, what should I do if I have to leave the interface as is?


Solution

  • Since you have a fixed set of classes, you could use the visitor pattern, which works without explicit downcasts.

    class WeaponVisitor {
       void visit(Sword aSword) { }
       void visit(ChargeGun aGun) { }
    }
    
    // add accept method to your Weapon interface
    interface Weapon {
      ...
      void accept(Visitor v);
    }
    
    // then implement accept in your implementing classes
    class Sword {
    ...
       @Override
       void accept(Visitor v) {
          v.visit(this); // this is instanceof Sword so the right visit method will be picked
       }
    }
    
    // lastly, extend Visitor and override the methods you are interested in
    class OnlySwordVisitor extends Visitor {
         @Override void visit(Sword aSword) {
          System.out.println("Found a sword!");
          aSword.wipeBloodfromSword();
         }
     }