ZOFTINO.COM android and web dev tutorials

Java Interfaces

Java interfaces allow you to decouple class implementation from its interface which the calling programs use to communicate with them, so that various implementations can be created and used without worrying about the calling programs. This leads to building applications which are easy to enhance.

Interface makes multiple inheritance of type possible because classes can implement multiple interfaces. Interfaces can contains method signatures, constants, default methods, static methods and nested types. Only static and default methods can have body in interfaces and all other methods are abstract.

Table of Contents

Defining Interface

Interface is declared using interface keyword and adding definitions of allowed elements in it. You can apply public or default access modifiers to interfaces. Default or no access modifier interface can only be accessed within the package, whereas the public interfaces can be used from anywhere.

Methods declarations in interface are automatically public and non static and non default methods of interface are implicitly abstract. So, you don’t need to use public and abstract keywords in the interface declarations.

For example, ShoppingCart interface is defined with four methods.

public interface ShoppingCart {
	
	public void addItem();
	public void remoteItem();
	public double calculateTotal();
	public double applyDiscount();
	
}

Implementing Interface

Class can implement interface and add behavior to abstract methods declared in it. The class which implements an interface needs to override all the methods declared in the interface. NormalShoppingCart class implements ShoppingCart interface.

public class NormalShoppingCart implements ShoppingCart{
	
	Map<String, Item> cartItems = new HashMap<String, Item>();

	@Override
	public void addItem(Item item) {
		cartItems.put(item.getId(), item);
	}

	@Override
	public void remoteItem(String itemId) {
		cartItems.remove(itemId);
	}

	@Override
	public double calculateTotal() {
		double totalOrderAmt = 0;
		for(String id :  cartItems.keySet()) {
			totalOrderAmt = totalOrderAmt + cartItems.get(id).getPrice();
		}
		return totalOrderAmt;
	}

	@Override
	public double applyDiscount() {	
		return 0;
	}
}

Interface Type

Interface can be used as a type. An interface can reference to an object of any class which implements the interface. Using the interface type, implementation methods can be accessed.

In the below example, ShoppingCart interface variable is assigned an instance of NormalShoppingCart implementation class.

		ShoppingCart sc =  new NormalShoppingCart();
		
		Item i = new Item();
		i.setId("tx7687bb");
		i.setName("mobile phone x");
		i.setPrice(235.99);
		
		sc.addItem(i);
		
		Item it = new Item();
		it.setId("rt3434uy");
		it.setName("mobile phone z");
		it.setPrice(375.33);
		
		sc.addItem(it);
		
		sc.calculateTotal();
		
		System.out.println("total order amount "+sc.calculateTotal());

Output:

total order amount 611.3199999999999

The ability to use interface as type and assign an instance of any implementation of the interface is the basis for dependency injection frameworks. Any implementation of an interface can be injected to a variable of the interface type. This feature make it is easy to build applications which can be enhanced with minimal effort.

Interface with Constants

You can declare fields in interfaces. All fields are implicitly public, static and final. So if you declare a filed in an interface, it is automatically considered as a constant. You can access fields of interface using interface type.

public interface OfferProvider {
	String cashback = "Cashback";
	String coupon = "coupon";
}

Here is how interface constants are accessed.

System.out.println("Interface constant value "+OfferProvider.cashback);

Output:

Interface constant value Cashback

You can access constants defined in interface in non static way.

OfferProvider op =  new CouponOfferProvider();
System.out.println("Interface constant value "+op.cashback);

Interface with Static Methods

Interfaces can have static methods in addition to abstract methods. Static methods are useful to provide common behavior and to evolve interfaces. If you add a static method to an existing interface, it will not break existing implementation classes of it.

For example, Taxer interface has one static method and one abstract method declarations.

public interface Taxer {

	public static double calculateTaxNormal(double taxableAmt) {
		return taxableAmt*30/100;
	}
	
	public double calculateTax(double taxableAmt);
}

Static methods in interfaces, like static methods in classes, can be directly accessed using Interface.

	double taxAmt = Taxer.calculateTaxNormal(taxableAmt);
		System.out.println("normal tax "+taxAmt);

Static methods defined in an interface are not visible in implementation classes. For example, if a class TaxerImpl implements Taxer interface, you can’t call TaxerImpl.calculateTaxNormal(taxableAmt).

Interface with Default Methods

Interfaces can contain non static methods with body. These methods are called default methods. Default methods, like other methods declarations, are by default public. To define default methods you need to use default keyword before the method signature.

The purpose of default methods is to provide the capability to enhance interfaces after they are defined and used. With default methods, an interface can be added additional methods at later stages without breaking the implementation classes.

To access default methods, first you need to create an instance of one of the implementation classes of the interface, assign the object to interface type and then call default methods on it.

For example, getBestOffer() method is a default method in OfferProvider interface.

public interface OfferProvider {
	default String getBestOffer() {
		return "flat 10% off";
	}
	List<String> getLatestOffers();
}

Accessing the default methods.

	OfferProvider op =  new CouponOfferProvider();
		System.out.println("default offer "+op.getBestOffer());

Output :

default offer flat 10% off

You can override default methods in the implementation class.

public class CouponOfferProvider implements OfferProvider {

	@Override
	public List<String> getLatestOffers() {
			…
	}
	@Override
	public String getBestOffer() {
		return "90% off";
	}
}

Now output will be:

default offer 90% off

Interface Extends Interface

Interfaces can extend any number of other interfaces. For example, OffersProvider interface extends CashbackProvider, CouponProvider and DealProvider interfaces. In addition to methods defined in OffersProvider interface, the class which implements OffersProvider interface needs to implement all methods that the interface inherited by extending other interfaces.

public interface CashbackProvider {
	String getMaxCahbackOffer();
}
public interface CouponProvider {
	String getBestCoupon();
}
public interface DealProvider {
	String getLatestDeal();
}

OffersProvider interface extends above three interfaces.

public interface OffersProvider extends CouponProvider, CashbackProvider, DealProvider{
	String getBestOffer();
}

Class which implements OffersProvider interface needs to implement all methods.

public class DefaultOffersProvider implements OffersProvider{

	@Override
	public String getBestCoupon() {
		return "best coupon";
	}

	@Override
	public String getMaxCahbackOffer() {

		return "max cashback";
	}

	@Override
	public String getLatestDeal() {

		return "latest deal";
	}

	@Override
	public String getBestOffer() {

		return "best offer";
	}

}
	OffersProvider offersP = new DefaultOffersProvider();
		
		System.out.println("method from CouponProvider interface "
							+offersP.getBestCoupon());
		System.out.println("method from OffersProvider interface "
							+offersP.getBestOffer());
		System.out.println("method from CashbackProvider interface "
							+offersP.getMaxCahbackOffer());

Extending Interfaces with Default Methods

If an interface extends another interface that has a default method, the default method from parent interface can be used as it is without any declaration, or default method can be overridden by defining default method as abstract or re-implementing it as default method in the interface.

For eample, PaymentProvider has default method.

public interface PaymentProvider {
	default List<String> getSupporedPaymentTypes() {
		List<String> payTypes = new ArrayList<String>();
		payTypes.add("credit");
		payTypes.add("cash");
		return payTypes; 
	}
	int getCharges();
}

NewPaymentProvider interface extends PaymentProvider and inherits the default method from parent.

public interface NewPaymentProvider extends PaymentProvider{
	int getRateOfInterest();
}

NewPaymentProvider interface can declare the default method from parent interface as non default or abstract method.

public interface NewPaymentProvider extends PaymentProvider{
	List<String> getSupporedPaymentTypes();
	int getRateOfInterest();
}

Or it can override the default method and provide its own implementation as a default method.

public interface NewPaymentProvider extends PaymentProvider{
	default List<String> getSupporedPaymentTypes() {
		List<String> payTypes = new ArrayList<String>();
		payTypes.add("debit");
		payTypes.add("visa");
		payTypes.add("master");
		return payTypes; 
	}
	int getRateOfInterest();
}

If an interface extends two interfaces and both of them have a default method with the same signature, then compiler will throw an error. You need to override the method in the interface.

For example, CashbackProvider and CouponProvider interfaces have common default method.

public interface CashbackProvider {
	String getMaxCahbackOffer();
	
	default List<String> getTypes() {
		List<String> types = new ArrayList<String>();
		types.add("direct");
		types.add("reimburse");
		return types; 
	}
}
public interface CouponProvider {
	String getBestCoupon();
	
	default List<String> getTypes() {
		List<String> types = new ArrayList<String>();
		types.add("online");
		types.add("retail");
		return types; 
	}
}

OffersProvider interface implements CouponProvider and CashbackProvider and it needs to override the common default method.

public interface OffersProvider extends CouponProvider, CashbackProvider{
	String getBestOffer();

	@Override
	default List<String> getTypes() {
		List<String> types = new ArrayList<String>();
		types.addAll( CouponProvider.super.getTypes());
		types.addAll( CashbackProvider.super.getTypes());
		return types;
	}
}

Functional Interfaces

Interfaces which have one method are called functional interfaces. Functional interface is a fundamental element behind the lambda expressions.

public interface Pricer {
	double getPrice(Product p);
}

Polymorphism

An interface type can be assigned an instance of any of its implementation classes. When a method is called on the interface type, which implementation is called depends on the object to which the interface type is referencing. This behavior is called virtual method invocation.

For example CreditCardPaymentProvider and DebitCardPaymentProvider classes implent PaymentProvider interface and provide behavior for getCharges method. Calling the method on PaymentProvider type gives different results depending on the object it is referencing to.

public interface PaymentProvider {
	int getCharges();
}
public class CreditCardPaymentProvider implements PaymentProvider {
	public int getCharges() {
		return 100;
	}
}
public class DebitCardPaymentProvider implements PaymentProvider {
	public int getCharges() {
		return 50;
	}
}
		PaymentProvider credit = new CreditCardPaymentProvider();
		PaymentProvider debit = new DebitCardPaymentProvider();	
		
		System.out.println("charges : "+credit.getCharges());
		System.out.println("charges : "+debit.getCharges());

Output:

charges : 100
charges : 50

Declaring Interface within Interface or Nested Interface

An interface can be declared inside another interface in Java. Inner interface is implicitly static; if not there is no other way to access it. The most common use of defining an interface inside another interface comes into picture when the enclosing interface depends on the inner interface and inner interface is used within the context of outer interface.

For example, a method of ProductPricer interface depends on another interface DiscountProvider which is exclusively used in the context of ProductPricer, then declaring DiscountProvider interface as inner interface makes sense.

public interface ProductPricer {
	double getPrice(DiscountProvider dp, double price);
	
	interface DiscountProvider{
		int getDiscountPercentage();
	}
}

Implementation of outer interface

public class ElectronicsProductPricer implements ProductPricer{

	@Override
	public double getPrice(DiscountProvider dp, double price) {
		int disPct = dp.getDiscountPercentage();
		
		return price*(100-disPct)/100;
	}
}

Implementation of inner interface

public class CommonDiscountProvider implements 
						ProductPricer.DiscountProvider {

	@Override
	public int getDiscountPercentage() {		
		return 5;
	}

}
		double productPrice = 345;
		
		ProductPricer pp = new ElectronicsProductPricer();
		ProductPricer.DiscountProvider dp = new CommonDiscountProvider();
		
		double finalPrice = pp.getPrice(dp, productPrice);
		System.out.println("final price : "+finalPrice);

Interface inside Class

Interfaces can be declared inside a class. It is useful to do so if the interface is only used inside the class and nested class implements it. Please see Java nested classes tutorial for examples.