Declaring Interfaces
An interface is a reference type similar to an abstract class but it can contain only:- Constants. All fields defined in an interface are implicitly
public
,static
, andfinal
(these modifiers can and should be omitted) - Abstract methods to be implemented by classes implementing the interface. Except for
default
methods, all methods declared in an interface are implicitlypublic
andabstract
(both can be omitted). They can’t bestatic
. - Default methods. Declared with the
default
modifier. They are methods with implementation. - Nested types (you can learn more about them in the lesson about nested classes)
The following pseudocode summarizes the basic syntax for an interface declaration:
1 2 3 4 5 6 7 8 9 |
[public] interface InterfaceName [extends Interface2, …, InterfaceN] { [constants] [abstract methods] [default methods] } |
Interfaces are implicitly abstract
and they:
- Can be only
public
or default (no access modifier), because they have to be visible to be implemented) - Can’t be
final
(they always have to be extendable) - Can extend more than one interface (unlike classes that can only extend one superclass)
The following is a basic interface example, including a default method:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
interface Device { // fields = constants (they are implicitly public, static and final) int MOBILE_DEVICE = 1; int DESKTOP_DEVICE = 2; // abstract method int getType(); // default method default String getName() { return "Device " + getType(); } } |
You must also know that empty interfaces are valid (and are usually used as object tags):
1 2 3 4 5 6 7 |
interface java.io.Serializable { } class MyFile implements Serializable { … } |
More About Default Methods
Default methods allow adding new methods to an existing interface without breaking backwards compatibility. Pre-existing classes implementing that interface are free to use or not the new method, but they are not required to be modified in order to keep working.If you extend an interface that contains default methods, the choices for each of them are:
- Override it, the same way a class would override a superclass method
- Inherit it by not mentioning it at all in your extended interface
- Re-declare it, which makes it abstract
Now that you know how to declare interfaces, let’s see how to use them.
Using Interfaces
Accessing Interface Constants
Interface constants are inherited by implementing classes. These constants (static
fields) can be accessed in different ways. The code example below illustrates this concept:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
interface I { int CONSTANT = 1; } // access from class implementing the interface class A implements I { void foo() { int c1 = this.CONSTANT; // access to class field int c2 = CONSTANT; // same as previous } } // access from class not implementing the interface class B { public static void main(String[] args){ A a = new A(); int c1 = a.CONSTANT; // through a reference (public field access) int c2 = I.CONSTANT; // static access int c3 = A.CONSTANT; // static access } } |
Interface References
We could say an object has multiple types when it implements one or more interfaces: its own class and all those interfaces. Therefore an interface reference can be used anywhere a type reference can be used: variables, fields, parameters, return values, etc. The referenced value can be any object of a class that implements the interface and you can use type casting to tell the compiler what the object really is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
interface I { } class A implements I { } ... A a = new A(); I i; // all the following are valid assignments and castings // since "an A is always an I" i = a; i = (A) a; i = (I) a; |
Remember: if you define a reference variable whose type is an interface, any object you assign to it must be an instance of a class that implements that interface.
Interface Type Is Inherited
If a classB
extends from a class A
implementing the interface I
, then B
is also an I
:
1 2 3 4 5 |
class A implements I { } class B extends A { } // B implements I implicitly !! |
You can also use a redundant declaration in the subclass and it’s still valid (and you don’t have to implement the interface methods again in the subclass):
1 2 3 4 5 6 7 8 9 10 11 12 13 |
interface I { void foo(); } class A implements I { public void foo() { System.out.println("foo"); } } class B extends A implements I { } // redundant declaration |
Finally, in case you don’t know, multiple casting is allowed by Java so the following code is also valid:
1 2 3 4 5 |
A a = new A(); B b = new B(); A = (B)(I) b; // double casting example |
In this example:
- The first casting –
(I) b
– is OK becauseb
is always anI
(sinceB
implementsI
) - The second casting –
(B)((I) b)
– is also OK because((I) b)
is anI
reference and so it could be aB
at runtime (and it is in this case)
You probably will find code like this in the exam and now that you know multiple casting is allowed it won’t catch you off guard!