Search code examples
javainheritancesubclasssuperclassimplements

Downcasting object to interface


In short, I'd like to be able to group class instances by a superclass which does not implement a certain interface. But from the set of instances I'd like to call methods from the interface on those instances that implement that interface.

Some example code which might explain it.

class Building{
  String c = "white"; 
  Building(){
  }

  void printColor(){
    println("The building is " + c);
  }

  void paint( String c ){
   this.c = c;  
  }

  void printBuildQuality(){
   println("The build quality is average"); 
  }
}


class SturdyFactoryBuilding extends Building implements Factory{

 SturdyFactoryBuilding(){
  super(); 
 }

 void printBuildQuality(){
  println("The build quality is sturdy"); 
 }

 void printFactoryOutput(){
  println("This factory makes stuff"); 
 }
}

class ShakyFactoryBuilding extends Building implements Factory{

  ShakyFactoryBuilding(){
   super(); 
  }

  void printBuildQuality(){
   println("The build quality is shaky");
  }

  void printFactoryOutput(){
   println("This factory makes slightly different stuff"); 
  }
}


public interface Factory{

  public void printFactoryOutput();

}

 Building building = new SturdyFactoryBuilding();    
 building.printBuildQuality();
 building.printColor();
 building.paint("bright red");
 building.printColor();
 building.printFactoryOutput();  

Is there a way I can achieve this, perhaps by having an 'isFactory' flag in the superclass.

Thanks.


Solution

  • I think you'll have to make a trade-off: Either you accept some anti-pattern or you open up you Building "interface" to act as an Adapter:

    class Building implements Factory{
    
        // the other building stuff
    
        @Override
        public void printFactoryOutput(){ /* NO OP */ }
    }
    

    Then you can call printFactoryOutput on all Buildings having no effect up to this point.

    Since your Factory-implementations extend Building they automatically inherit the NOOP-Implementation. But since you override it:

    class ShakyFactoryBuilding extends Building implements Factory{
    
      ShakyFactoryBuilding(){
       super(); 
      }
    
      @Override
      public void printBuildQuality(){
       println("The build quality is shaky");
      }
    
      @Override
      public void printFactoryOutput(){
       println("This factory makes slightly different stuff"); 
      }
    }
    

    ... you have the desired result.

    Drawback is of course that all Buildings do have the printFactoryOutput visible. But that's the trade-off I was talking about. If this is not acceptable, you'll have to completely reconsider your design.

    To make it clear that a Building that is not a Factory shall not be called that Method on, you could throw an UnsupportedOperationException in Building, but that would force try/catch blocks everywhere in your code. You could as well return a boolean: default=false and returning true if in fact a factory ... There are plenty possibilities.

    You could also change your design to use composition over inheritance.