Let's say you want to define an interface that allows publishing / consuming data to and from different types of channels (e.g. AMQP, JMS, etc), but you want to be able to switch out the implementation details
Let's define a basic IO interface that can be re-used across multiple implementations:
public interface IO<IncomingType, OutgoingType> { void publish(OutgoingType data); IncomingType consume(); IncomingType RPCSubmit(OutgoingType data); }
Now I can instantiate that interface, but since we don't have default implementations for those methods, it'll need an implementation when we instantiate it:
IO<String, String> mockIO = new IO<String, String>() { private String channel = "somechannel"; @Override public void publish(String data) { System.out.println("Publishing " + data + " to " + channel); } @Override public String consume() { System.out.println("Consuming from " + channel); return "some useful data"; } @Override public String RPCSubmit(String data) { return "received " + data + " just now "; } }; mockIO.consume(); // prints: Consuming from somechannel mockIO.publish("TestData"); // Publishing TestData to somechannel System.out.println(mockIO.RPCSubmit("TestData")); // received TestData just no
We can also do something more useful with that interface, let's say we want to use it to wrap some basic RabbitMQ functions:
public class RabbitMQ implements IO<String, String> { private String exchange; private String queue; public RabbitMQ(String exchange, String queue){ this.exchange = exchange; this.queue = queue; } @Override public void publish(String data) { rabbit.basicPublish(exchange, queue, data.getBytes()); } @Override public String consume() { return rabbit.basicConsume(exchange, queue); } @Override public String RPCSubmit(String data) { return rabbit.rpcPublish(exchange, queue, data); } }
Let's say I want to use this IO interface now as a way to count visits to my website since my last system restart and then be able to display the total number of visits - you can do something like this:
import java.util.concurrent.atomic.AtomicLong; public class VisitCounter implements IO<Long, Integer> { private static AtomicLong websiteCounter = new AtomicLong(0); @Override public void publish(Integer count) { websiteCounter.addAndGet(count); } @Override public Long consume() { return websiteCounter.get(); } @Override public Long RPCSubmit(Integer count) { return websiteCounter.addAndGet(count); } }
Now let's use the VisitCounter:
VisitCounter counter = new VisitCounter(); // just had 4 visits, yay counter.publish(4); // just had another visit, yay counter.publish(1); // get data for stats counter System.out.println(counter.consume()); // prints 5 // show data for stats counter page, but include that as a page view System.out.println(counter.RPCSubmit(1)); // prints 6
When implementing multiple interfaces, you can't implement the same interface twice. That also applies to generic interfaces. Thus, the following code is invalid, and will result in a compile error:
interface Printer<T> { void print(T value); } // Invalid! class SystemPrinter implements Printer<Double>, Printer<Integer> { @Override public void print(Double d){ System.out.println("Decimal: " + d); } @Override public void print(Integer i){ System.out.println("Discrete: " + i); } }
class SomeClass { } class Demo<T extends SomeClass> { } But a type parameter can only bind to a single class type. An interface type can be bound to a type that already had a binding. This is achieved using the & symbol: interface SomeInterface { } class GenericClass<T extends SomeClass & SomeInterface> { } This strengthens the bind, potentially requiring type arguments to derive from multiple types. Multiple interface types can be bound to a type parameter: class Demo<T extends SomeClass & FirstInterface & SecondInterface> { } But should be used with caution. Multiple interface bindings is usually a sign of a code smell, suggesting that a new type should be created which acts as an adapter for the other types: interface NewInterface extends FirstInterface, SecondInterface { } class Demo<T extends SomeClass & NewInterface> { }
A method defined in an interface is by default public abstract. When an abstract class implements an interface, any methods which are defined in the interface do not have to be implemented by the abstract class. This is because a class that is declared abstract can contain abstract method declarations. It is therefore the responsibility of the first concrete sub-class to implement any abstract methods inherited from any interfaces and/or the abstract class.
public interface NoiseMaker { void makeNoise(); } public abstract class Animal implements NoiseMaker { //Does not need to declare or implement makeNoise() public abstract void eat(); } //Because Dog is concrete, it must define both makeNoise() and eat() public class Dog extends Animal { @Override public void makeNoise() { System.out.println("Borf borf"); } @Override public void eat() { System.out.println("Dog eats some kibble."); } }
From Java 8 onward it is possible for an interface to declare default implementations of methods which means the method won't be abstract, therefore any concrete sub-classes will not be forced to implement the method but will inherit the default implementation unless overridden.
Learn All in Tamil © Designed & Developed By Tutor Joes | Privacy Policy | Terms & Conditions