--- title: Introduction of Interface in Spring Boot and Java8 description: --- # Introduction of Interface in Spring Boot and Java8 ###### Author: TYT ###### Tags: `Reading notes`, `Java`, `Spring Boot`,`Interface` - [Introduction](#Introduction) - What, why, when, and how - [Usage](#Usage) - Single and muliple implement (inter-, intra, and unit test) - [Misuse and overuse](#Misuse-and-overuse) - [Summary](#Summary) - [Quiz](#Quiz) --- ## Introduction ### What is interface? - Interface is a reference type. - Contain method signature, default method, and static method. - Cannot be instantiated, only be implemented by class and extended by interface. ```java public interface RoadVehicle { String drive(String direction); // can be used when class implemented. default void turnoff() { System.out.println("turn off"); } // can be used without implementaton. static void honk() { System.out.println("Do Do!"); } } ``` ### Why use interface? - Achieve - Loose coupling :::spoiler Tight coupling vs. Loose coupling - Tight coupling ```java public class Person { public static void main(String[] args){ Shopping shopping = new Shopping(); shopping.pay(); } } ``` We have to commit and uncommit for switching payment instrument to pay. Otherwise, creat more shopping class, Shopping1 and Shopping2, etc. to cater to payment instrument instead of commit and uncommit. ```java public class Shopping { public void pay(){ CreditCard creditCard = new CreditCard(); creditCard.pay(); // SmartPhone smartPhone = new SmartPhone(); // smartPhone.pay(); } } ``` ```java public class CreditCard { public void pay(){ System.out.println("Pay by credit card."); } } ``` ```java public class SmartPhone { public void pay(){ System.out.println("Pay by smart phone."); } } ``` - Loose coupling Person can controll the payment instrument he/she want, kind of IoC I think. ```java public class Person { public static void main(String[] args){ PaymentInstrument creditCard = new CreditCard(); // PaymentInstrument smartPhone = new SmartPhone(); Shopping shopping = new Shopping(creditCard); shopping.pay(); } } ``` Creat variable to switch payment instrument easier. ```java public class Shopping { private PaymentInstrument paymentInstrument; public Shopping(PaymentInstrument paymentInstrument){ this.paymentInstrument = paymentInstrument; } public void pay() { paymentInstrument.pay(); } } ``` ```java public interface PaymentInstrument { void pay(); } ``` ```java public class CreditCard implements PaymentInstrument{ @Override public void pay(){ System.out.println("Pay by credit card."); } } ``` ```java public class SmartPhone implements PaymentInstrument{ @Override public void pay(){ System.out.println("Pay by smart phone."); } } ``` ::: ::: spoiler Recommended vedio {%youtube ifufbHjIQCo %} ::: - Multiple implement and interitance - Switch implement easily ### When use interface - Specify the "behavior" of a particular data type - Expect the unrelated class whould implenment the interface - Storage device (interface) - Optical disc (1965), Floppy disk drive (1971), and USB flash drive (2000), etc.(class) - Multiple implement and interface - Class USB flash drive implenments interface storage device and charging equipment. ### How to use interface? - Interface :::spoiler Use "extends" to extend the interface. ``` java public interface I002 extends I001 { //TODO } ``` ::: :::spoiler Use "," when mutiple interface is extended. ``` java public interface I003 extends I001, I002 { //TODO } ``` ::: - Class :::spoiler Use "implenments" to implenments the interface ``` java public class C001 implenments I001 { //TODO } ``` ::: :::spoiler Use "," when mutiple interface is implenmented. ``` java public interface C002 implenments I001, I002 { //TODO } ``` ::: :::spoiler Should create all method body which method signature is declared in interface. ``` java public interface I001 { String greeting (); } ``` ```java public class C001 implenments I001 { publc greeting (){ return "hello"; } } ``` ::: ## Usage ### One implement :::spoiler Instantiate implement in native Java, likes ```List<int> list = new ArrayList<>()```, ```Map<String, Object> map = new HasMap<>()```, etc. ```java @RestController @RequestMapping("/One") public class PersonA1Controller { @RequestMapping(value = "/PersonA1", method = RequestMethod.GET) public String PersonA1(){ PaymentInstrumentA paymentInstrumentA = new CreditCardA(); return paymentInstrumentA.pay(); // Pay by credit card A. } } ``` ``` java public interface PaymentInstrumentA { String pay (); } ``` ```java @Service public class CreditCardA implements PaymentInstrumentA { @Override public String pay(){ return "Pay by credit card A."; } } ``` ::: :::spoiler Use @Service/ @Component in Spring Boot ```java @RestController @RequestMapping("/One") public class PersonA2Controller { @Autowired PaymentInstrumentA paymentInstrumentA; @RequestMapping(value = "/PersonA2", method = RequestMethod.GET) public String PersonA2(){ return paymentInstrumentA.pay(); } } ``` ``` java public interface PaymentInstrumentA { String pay (); } ``` ```java @Service public class CreditCardA implements PaymentInstrumentA { @Override public String pay(){ return "Pay by credit card A."; } } ``` ::: ### Multiple implement - Inter-class :::spoiler Use default name (lowercase in the first letter) as variable name. ```java @RestController @RequestMapping("/Multiple/Inter") public class PersonB1Controller { @Autowired PaymentInstrumentB creditCardB1ServiceImpl; @RequestMapping(value = "/PersonB1", method = RequestMethod.GET) public String PersonB1(){ return creditCardB1ServiceImpl.pay(); // Pay by credit card B1. } } ``` ```java public interface PaymentInstrumentB { String pay(); } ``` ``` java @Service public class CreditCardB1ServiceImpl implements PaymentInstrumentB { @Override public String pay() { return "Pay by credit card B1."; } } ``` ::: :::spoiler Name implenment and use @Qualifier to specify default implenment ```java @RestController @RequestMapping("/Multiple/Inter") public class PersonB2Controller { @Qualifier("CreditCardB2ServiceImpl") @Autowired PaymentInstrumentB paymentInstrumentB; @RequestMapping(value = "/PersonB2", method = RequestMethod.GET) public String pay(){ return paymentInstrumentB.pay(); // Pay by credit card B2. } } ``` ```java public interface PaymentInstrumentB { String pay(); } ``` ``` java @Service("CreditCardB2ServiceImpl") public class CreditCardB2ServiceImpl implements PaymentInstrumentB { @Override public String pay() { return "Pay by credit card B2."; } } ``` ::: :::spoiler Use @Primary to specify default implenment ```java @RestController @RequestMapping("/Multiple/Inter") public class PersonB3Controller { @Autowired PaymentInstrumentB creditCardCService; @RequestMapping(value = "/PersonB3", method = RequestMethod.GET) public String pay(){ return creditCardCService.pay(); // Pay by credit card B3. } } ``` ```java public interface PaymentInstrumentB { String pay(); } ``` ``` java @Primary @Service public class CreditCardB3ServiceImpl implements PaymentInstrumentB { @Override public String pay() { return "Pay by credit card B3."; } } ``` ::: - Intra-class :::spoiler Instantiate implenment to switch ```java @RestController @RequestMapping("/Multiple/Intra") public class PersonCController { @Qualifier("CreditCardC1") @Autowired PaymentInstrumentC paymentInstrumentC; @RequestMapping(value = "/PersonC1", method = RequestMethod.GET) public String payD001(){ return paymentInstrumentC.pay(); // Pay by credit card C1. } @RequestMapping(value = "/PersonC2", method = RequestMethod.GET) public String payD002(){ PaymentInstrumentC paymentInstrumentC = new CreditCardC2(); return paymentInstrumentC.pay(); // Pay by credit card C2. } } ``` ``` java public interface PaymentInstrumentC { String pay(); } ``` ```java //@Primary @Service("CreditCardC1") public class CreditCardC1 implements PaymentInstrumentC { @Override public String pay() { return "Pay by credit card C1."; } } ``` ```java @Service("CreditCardC2") public class CreditCardC2 implements PaymentInstrumentC { @Override public String pay() { return "Pay by credit card C2."; } } ``` ::: - Will Run build failed if don't use @Primary, @Qualifer, and default name, e.g. [stackoverflow](https://stackoverflow.com/questions/46343560/class-required-a-single-bean-but-2-were-found). ``` Description: Field {interfaceName} in {class which declars interface}required a single bean, but 2 were found: {implenment01}: defined in fill {file path} {implenment02}: defined in fill {file path} Action: Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed ``` - With unit test - App code - Use @Primary/ name implenment, and use @Qualifier/ default name to specify implenment. ```java @RestController @RequestMapping("Multiple/UnitTest") public class PersonDController { @Autowired PaymentInstrumentD paymentInstrumentD; @RequestMapping(value = "/PersonD", method = RequestMethod.GET) public String pay(){ return paymentInstrumentD.pay(); // Pay by credit card D. } } ``` ```java public interface PaymentInstrumentD { String pay(); } ``` ```java // @Primary @Service public class CreditCardD implements PaymentInstrumentD { @Override public String pay() { return "Pay by credit card D."; } } ``` - Unit test code :::spoiler use default name/ @Qualifier to specify implenment. - default name ```java @RunWith(SpringRunner.class) @ContextConfiguration(classes=ShareApplication.class) public class PersonDControllerTest1 { @Autowired PaymentInstrumentD creditCardDTest1; @Test public void pay(){ System.out.println(creditCardDTest1.pay()); } } ``` ```java @Service public class CreditCardDTest1 implements PaymentInstrumentD { @Override public String pay() { return "Pay by credit card D Test 1."; } } ``` - @Qualifier ```java @RunWith(SpringRunner.class) @ContextConfiguration(classes=ShareApplication.class) public class PersonDControllerTest2 { @Qualifier("CreditCardDTest2") @Autowired PaymentInstrumentD paymentInstrumentD; @Test public void pay(){ System.out.println(paymentInstrumentD.pay()); } } ``` ```java @Service("CreditCardDTest2") public class CreditCardDTest2 implements PaymentInstrumentD { @Override public String pay() { return "Pay by credit card D Test 2."; } } ``` ::: :::spoiler Will get return from app code if do not use default name/ @Qualifier to specify implenment. ```java @RunWith(SpringRunner.class) @ContextConfiguration(classes=ShareApplication.class) public class PersonDControllerTest2 { @Qualifier("CreditCardDTest2") @Autowired PaymentInstrumentD paymentInstrumentD; @Test public void pay(){ System.out.println(paymentInstrumentD.pay()); } } ``` ```java @Service("CreditCardDTest2") public class CreditCardDTest2 implements PaymentInstrumentD { @Override public String pay() { return "Pay by credit card D Test 2."; } } ``` ::: - App code still get return from app code's implenment, even unit test use @Primary. - Will Run build failed if don't use @Primary, @Qualifer, and default name. ``` Description: Field {interfaceName} in {class which declars interface}required a single bean, but 2 were found: {implenment01}: defined in fill {file path} {implenment02}: defined in fill {file path} Action: Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed ``` - If app code and unit test code both use @primary - Run build will be succussed. - Run test will be failed. ## Misuse and overuse - Create interface just in case. - Create interface is only for a class. - Create all method body which declare in interface. - Name implenment after interface. - bad ``` java public interface PaymentInstrument { String pay(); } ``` ```java @Service public class PaymentInstrumentImpl implenments PaymentInstrument { publc pay(){ return "Pay by paymentInstrumentImpl!"; } } ``` - better ``` java public interface PaymentInstrument { String pay(); } ``` ```java @Service public class CreditCardVisa implenments PaymentInstrument { publc pay(){ return "Pay by Visa!"; } } ``` ## Summary - Interface characteristics - Loose coupling. - Multiple implenment and ingeritance. - Switch implenment easily. - Usage - Create a interface to extend interface. - Create a class to implenment interface. - Use @Primary or name implenment, e.g. @Service("serviceName") and @Component("componentName") - Use default name or @Qualifier to specify implenment when declare interface. - Misuse and overuse - Avoid using without planing. - Create interface is only for a class. ## Quiz - Output of following Code (A) Hello! (B) Hi! ```java public class Main { @Autowired IService i002ServiceImpl; publc static void main(String[] args){ iService.greeting(); } } ``` ``` java public interface IService { String greeting (); } ``` ```java @Primary @service("I001") public class I001ServiceImpl extends Base { publc void greeting(){ System.out.println("Hello!"); } } ``` ```java @Service("I002") public class I002ServiceImpl extends Base { publc void greeting(){ System.out.println("Hi!"); } } ``` :::spoiler Answer (B) Hello! :::