Interfejs to typ, którego zadaniem jest rozłączenie implementacji działania od definicji parametrów wyjścia.wejścia.
Jest to specyficzny kontrakt, który jest implementowany przez klasy. Kontrakt ten zawiera tylko informacje o co dana metoda ma się zwrócić i co ma ona przyjąć. Definicja działania jest już określona w klasie.
Aby zdefiniować interfejs wystarczy użyć słowa kluczowego interface zamiast słowa class.
Nazewnictwo Interfejsów powinno zaczynać się z dużej litery. W C# konwencja mówi by interfejsy zaczynały się od litery “I”.
Gdy interfejs nie jest zagnieżdżony wewnątrz innego typu, to może mieć tylko dwa poziomy dostępu. Publiczny i na poziomie paczki/biblioteki.
interface MyInterface {}
Elementy interfejsu
Blok kodu wewnątrz interfejsu może posiadać tylko sygnatury metod. Te metody nie mogą mieć żadnych implementacji. Ich ciała są zastępowane średnikiem.
Metody interfejsu zawsze są publiczne dlatego nie można nadać im poziomu dostępu.
Interfejsy mogą zawierać stałe. Tyczy się to jednak tylko Javy.
interface MyInterface {
int cx = 10; // constant
}
Interfejs w Javie też może mieć zagnieżdżone klasy i interfejsy. W C# nie ma takiej opcji.
interface MyInterface
{
// Typy
class Class {}
interface Interface {}
enum Enum {}
}
interface MyInterface
{
class HelperClass {
public static void helperMethod() {}
}
}
W C#, w interfejsie można umieszczać tylko metody, delegaty, zdarzenia, indexery oraz właściwości. Właściwości to tak naprawdę dwie metody GET; SET; więc nie powinno to nikogo dziwić.
W Javie nie ma właściwości, delegat, zdarzeń i indexerów więc nie ma ich w interfejsie.
interface IMyInterface
{
// metoda
int GetArea();
// Właściwość
int Area { get; set; }
// Indexer
int this[int index] { get; set; }
// Zdarzenie
event System.EventHandler MyEvent;
}
public delegate void UpdateStatusEventHandler(string status);
public delegate void StartedEventHandler();
public interface IMyInterface
{
event UpdateStatusEventHandler StatusUpdated;
event StartedEventHandler Started;
}
Przykłady interfejsów
W C# mamy interfejs IComparable, który definiuje pojedynczą metodę Compare.
interface IComparable
{
int Compare(object o);
}
Klasa Circle, która znajduje się poniżej implementuje ten interfejs używając tej samej notacji co dziedziczenie.
Klasa Circle musi definiować metodę Compare. Dla tej klasy będzie to radius koła. Zaimplementowana metoda musi być publiczna. Dodatkowo sygnatura musi być identyczna do tej w interfejsie.
C# i Java może implementować wiele interfejsów.
W Javie istnieje podobny interfejs Comparable
Posiada on jedną metodę o nazwie “compare”.
interface Comparable
{
int compare(Object o);
}
Klasa poniżej implementuje ten interfejs używając słowa kluczowego implements po nazwie klasy.
Konwencja mówi by słowo kluczowe implements stawiać po wyrażaniu extends.
Klasa może implementować wiele interfejsów, ale może dziedziczyć tylko po jednej klasie.
Aby zadeklarować kolejne implementacje interfejsów używamy przecinka.
class Circle implements Comparable
{
public int r;
}
Klasa implantująca interfejs musi stworzyć definicję metod z interfejsów. Oczywiście.
class Circle implements Comparable
{
public int r;
public int compare(Object o) {
return r - ( (Circle)o ).r;
}
}
Funkcjonalność interfejsów
Te interfejsy obrazują i demonstrują jedno z użyć interfejsów. Definiują one specyficzną funkcjonalność wielu klas.
Istnieje możliwość używania interfejsów bez wiedzy jak dokładny typ klasy tam się znajduje.
Circle c1 = new Circle();
c1.r = 10;
Circle c2 = new Circle();
c2.r = 11;
IComparable compa1 = c1;
IComparable compa2 = c2;
Aby było to łatwiej zrozumieć poniżej znajduje się metoda, która spośród dwóch obiektów, które implementują ten interfejs wybierze największy z nich.
public static Object largest(Comparable a, Comparable b)
{
return (a.compare(b) > 0) ? a : b;
}
static object Largest(IComparable a, IComparable b)
{
return (a.Compare(b) > 0) ? a : b;
}
Interfejsy klas
Drugim sposobem użycia interfejsu jest dostarczenie kontraktu/spisu metod danej klasy. Interfejs wtedy opisuje, co dana klasa potrafi.
interface IMyClass
{
void Widoczne();
}
class MyClass : IMyClass
{
public void Wiidoczne() {}
public void Ukryte() {}
}
Programiści też wtedy potrafią widzieć daną klasę przez interfejs.
IMyInterface m = new MyClass();
Taka abstrakcja ma dwie zalety. Po pierwsze ułatwia ona pracę z klasami programistom, którzy chcą skorzystać tylko z konkretnych metod danej klasy.
Po drugie pozwala na uplastycznienie implementacji zakładając, że to, co jest w interfejsie nie ulegnie zmianie.