Akinkunle Allen
Akinkunle Allen

Reputation: 1309

Interface, Abstract Class and Methods of Abstract Class

I am learning how to use the Factory pattern for creating objects in Java. I want to create classes to manage Cars. A car can either be small or large. I created an interface that defines the methods to be implemented by an implementing class. An abstract class implements some of the common methods of the interface shared by small and large cars. The concrete SmallCar and LargeCar classes implement the remaining methods of the abstract class.

THE CAR INTERFACE

public interface Car {
 String getRegistrationNumber();
 void drive();
}

THE ABSTRACT CAR CLASS IMPLEMENTS CAR INTERFACE

public abstract class AbstractCar implements Car { 
 private final RegistrationNumber regNumber;
 private boolean tankFull = true;

 public AbstractCar(RegistrationNumber regNumber) {
  this.regNumber = regNumber;
 }

 @Override
 public final String getregistrationNumber() {
  return regNumber.toString();
 }

/**This method is not defined in the implemented Car interface. I added it to
*the abstract class because I want subclasses of these abstract class
*to have this method*/
 public boolean isTankFull() {
  return tankFull;
 }
}

SMALL CAR EXTENDS ABSTRACT CLASS

public final class SmallCar extends AbstractCar {
 public SmallCar(RegistrationNumber regNum) {
  super(regNum);
 }

 @Override
 public void drive() {
  //implemented here
 }
}

FACTORY CLASS:

This class is responsible for creating instances of a particular type of car.

public final class CarFactory {
 public static Car createCar(String carType, RegistrationNumber regNum) {
  Car car = null;
  if (carType.equals("Small") {
   car = new SmallCar(regNum);
  }
  return car;                
}

MAIN METHOD

RegistrationNumber regNum = new RegistrationNumber('a', 1234);
Car c = CarFactory.createCar("Small", regNum);

c.getRegistrationNumber(); //this works
c.isTankFull(); //this instance of Car cannot access the isTankFull method defined on the abstract class. The method is not defined on the Car interface though. I do not understand why.

The challenge is that the instance of Car can access every other method defined on the Car interface but it cannot access the isTankFull() method defined on the abstract class but not defined on the interface. I hope my explanation is clear enough.

Upvotes: 0

Views: 831

Answers (3)

riccardo.cardin
riccardo.cardin

Reputation: 8353

An interface is a contract (or a protocol) that you made with the users of the classes that implement it. So you have to ask to yourself if any Car should expose the information isTankFull (i.e. should respond to message isTankFull). If the answer is 'yes', then the method isTankFull must be added to the interface.

Looking at your code, it seems that the class AbstractCar is only a utility class. Then, the method isTankFull should be lift up to interface, or it should be made at least protected.

On the other hand you have to ask yourself if your client code, i.e. main method, really needs a generic Car, or if it needs instead a specific kind of car, such as a SmallCar.

Finally, rember that the use of interface lets you minimize dependency between your components.

Upvotes: 1

krillgar
krillgar

Reputation: 12805

The reason why you can't see the method there is because your c object is declared as a Car interface. Granted, when it comes out of your factory method, it is a SmallCar, but your variable there is only the interface. You could either change your declaration to AbstractCar c = CarFactory.createCar("SmallCar", regnum);.

Another way you could accomplish this while working with the interface would be to cast your c object to an AbstractCar when trying to access methods that are not on the interface, however you need to be careful as there is always the possibility that your factory could return an object that implements Car, but not AbstractCar.

if (c instanceof AbstractCar) {
    ((AbstarctCar)c).isTankFull();
}

Of course, the other easy solution would be to add the method to the interface, though that would remove the teaching opportunity from this question.

Upvotes: 2

antonio
antonio

Reputation: 18242

The good solution is to put your isTankFull() on the interface. It makes sense as any car implementing Car would need access to isTankFull().

The question is: are you creating any Car that will not be able to answer the question isTankFull? If so, then moving isTankFull to the interface will not make sense.

Another solution (if you don't want your isTankFull() to be on the interface), is to cast your Car to the appropriate type:

if (c instanceof AbstractCar) {
    ((AbstractCar)c).isTankFull();
}

Upvotes: 1

Related Questions