Java - Interfejs


U ovom lekciji naučićemo o Java interfejsu. Uz primjere ćemo naučiti kako implementovati interfejs i kada ih detaljno koristiti. Interfejs je potpuno apstraktna klasa koja uključuje grupu metoda bez tijela. U Javi interfejsi definiše skup specifikacija koje druge klase moraju implementovati. Pogledajmo primjer:

interface Language {
   public void getName();
}

Ovdje smo koristili ključnu riječ interface za stvaranje interfejsa nazvanog Language. Language interfejs definiše specifikaciju getName(). Sada bi svaka klasa koja koristi ovaj interfejs trebala implementovati specifikaciju getName().


Primjer: Java interfejs

// Kreiranje interfejsa
interface Language {
  void getName(String name);
}

// Klasa implementira interfejs
class ProgrammingLanguage implements Language {

  // Primjena apstraktne metode
  public void getName(String name) {
    System.out.println("Programski jezik: " + name);
  }
}

class Main {
  public static void main(String[] args) {
    ProgrammingLanguage language = new ProgrammingLanguage();
    language.getName("Java");
  }
}

U gornjem primjeru stvorili smo interfejs nazvan Language. Interfejs uključuje abstraktnu metodu getName(). Ovdje klasa ProgrammingLanguage implementuje interfejs i pruža implementaciju metode. Nije obavezno koristiti ključnu riječ abstract dok deklarišete apstraktne metode unutar interfejsa. To je zato što interfejs uključuje samo abstraktne metode, a ne uobičajene metode.



Implementacija interfejsa

Poput abstraktnih klasa, ne možemo stvoriti objekte interfejsa. Međutim, možemo implementovati interfejs. Za implementaciju interfejsa koristimo ključnu reč implements. Pogledajmo primjer:

// Kreiranje interfejsa
interface Polygon {
  void getArea(int length, int breadth);
}

// Implementacija Polygon interfejs
class Rectangle implements Polygon {

  // Implementacija abstraktne metode
  public void getArea(int length, int breadth) {
    System.out.println("Površina pravougaonika je " + (length * breadth));
  }
}

class Main {
  public static void main(String[] args) {
    // Kreiranje objekta
    Rectangle r1 = new Rectangle();
    r1.getArea(5, 6);
  }
}

U gornjem primjeru stvorili smo interfejs nazvano Polygon. Interfejs sadrži abstraktnu metodu getArea(). Ovdje klasa Rectangle implementuje Polygon i pruža implementaciju metode getArea().



Proširenje interfejsa

Slično klasama, interfejs se može proširiti u drugi interfejs. Ključna riječ extends koristi se za proširivanje interfejsa. Pogledajmo primjer:

interface Line {
  // Član od Line interfejsa
}

// Proširivanje interfejsa
interface Polygon extends Line {
  // Član od Polygon interfejsa
  // Član od Line interfejsa
}

Ovdje poligonski interfejs (Polygon) proširuje linijsko (Line) interfejs. Sada, ako bilo koja klasa implementuje Polygon, trebala bi osigurati implementacije svih apstraktnih metoda Line i Polygon.



Prednosti interfejsa u Javi

Sad kad znamo šta su interfejsi, naučimo zašto se interfejsi koriste u Javi.

  • Interfejsi pružaju specifikacije koje klasa (koja je implementira) mora sljediti.
  • U našem prethodnom primjeru koristili smo getArea() kao specifikaciju unutar interfejsa Polygon. Ovo je poput postavljanja pravila da bismo trebali moći dobiti površinu svakog pravougaonika.
  • Sada bilo koja klasa koja implementuje Polygon interfejs mora osigurati implementaciju metode getArea().
  • Slično apstraktnim klasama, interfejsi nam pomažu da postignemo abstrakciju u Javi.
  • Ovdje znamo kako getArea() izračunava površinu pravougaonika, ali način na koji se izračunava površina različit je za različite pravougaonike. Zbog toga je implementacija getArea() nezavisna jedna o drugoj.
  • Interfejs se takođe koriste za postizanje višestrukog nasljeđivanja u Javi. Pogledajmo primjer:

  • interface Line {
    ...
    }
    
    interface Polygon {
    ...
    }
    
    class Rectangle implements Line, Polygon {
    ...
    }
  • Ovdje klasa Rectangle implementuje dva različita interfejsa. Na ovaj način postižemo višestruko nasljeđivanje u Javi.


Zadane metode u Java interfejsima

Od izdanjem Jave 8, možemo dodati metode s implementacijom unutar interfejsa. Te se metode nazivaju zadanim metodama. Da bismo deklarisali zadane metode unutar interfejsa, koristimo ključnu riječ default. Pogledajmo primjer:

public default void getSides() {
   // Tijelo (body) od getSides()
}


Zašto zadane metode?

Uzmimo scenario da shvatimo zašto su zadane metode uvedene u Javi. Pretpostavimo da trebamo dodati novu metodu u interfejs. Metodu u naš interfejs možemo dodati lako bez implementacije. Međutim, to nije kraj priče. Sve naše klase koje implementuju taj interfejs moraju osigurati implementaciju metode. Ako je veliki broj klasa implementovao ovaj interfejs, moramo pratiti sve ove klase i unijeti promjene u njih. Ovo nije samo zamorno već i sklono greškama. Da bi to riješila, Java je uvela zadane metode. Zadane metode se nasljeđuju poput običnih metoda. Uzmimo primjer za bolje razumijevanje zadanih metoda.


Primjer: Zadana metoda u Java interfejsu

interface Polygon {
  void getArea();

  // Difoltna metoda
  default void getSides() {
    System.out.println("Mogu dobiti stranice mnogougla.");
  }
}

// Implementuje interfejs
class Rectangle implements Polygon {
  public void getArea() {
    int length = 6;
    int breadth = 5;
    int area = length * breadth;
    System.out.println("Površina pravougaonika je " + area);
  }

  // Poništava (overrides) getSides()
  public void getSides() {
    System.out.println("Ja imam 4 strane.");
  }
}

// Implementuje interfejs
class Square implements Polygon {
  public void getArea() {
    int length = 5;
    int area = length * length;
    System.out.println("Površina kvadrata je " + area);
  }
}

class Main {
  public static void main(String[] args) {

    // Kreiranje objekta od Rectangle
    Rectangle r1 = new Rectangle();
    r1.getArea();
    r1.getSides();

    // Kreiranje objekta od Square
    Square s1 = new Square();
    s1.getArea();
    s1.getSides();
  }
}

U gornjem primjeru smo stvorili interfejs nazvan Polygon. Ima zadanu metodu getSides() i abstraktnu metodu getArea(). Ovdje smo stvorili dvije klase Pravouganik (Rectangle) i kvadrat (Square) koji implementuju Polygon. Klasa Rectangle pruža implementaciju metode getArea() i poništava metodu getSides(). Međutim, klasa Square pruža samo implementaciju metode getArea(). Sada, dok pozivamo metodu getSides() pomoću objekta Rectangle, poziva se nadjačana metoda. Međutim, u slučaju objekta Square, poziva se zadana metoda.



Privatne (private) i statičke (static) metode u interfejsu

Java 8 je takođe dodala još jednu funkciju koja uključuje statičke metode unutar interfejsa. Slično klasi, mi možemo pristupiti statičkim metodama interfejsa koristeći njegove reference. Pogledajmo primjer:

// Kreiranje interfejsa
interface Polygon {
  staticMethod(){..}
}

// Pristupanje statičkoj (static) metodi
Polygon.staticMethod();


Praktični primjer interfejsa

Pogledajmo praktičniji primjer Java Interface-a.

// Za upotrebu sqrt funkcije
import java.lang.Math;

interface  Polygon {
   void getArea();
  
 // Izračunajte površina mnogougla
   default void getPerimeter(int... sides) {
      int perimeter = 0;
      for (int side: sides) {
         perimeter += side;
      }

   System.out.println("Površina je: " + perimeter);
   }
}

class Triangle implements Polygon {
   private int a, b, c;
   private double s, area;

// Inicijaliziacija stranica trokugla
   Triangle(int a, int b, int c) {
      this.a = a;
      this.b = b;
      this.c = c;
      s = 0;
   }

// Izračunajte površinu trokuta
   public void getArea() {
      s = (double) (a + b + c)/2;
      area = Math.sqrt(s*(s-a)*(s-b)*(s-c));
      System.out.println("Area: " + area);
   }
}

class Main {
   public static void main(String[] args) {
      Triangle t1 = new Triangle(2, 3, 4);

// Pozivanje metode Triangle klase (class)
      t1.getArea();

// Pozivanje metode Polygon
      t1.getPerimeter(2, 3, 4);
   }
}

U gore navedenom programu stvorili smo interfejs nazvan Polygon. Uključuje zadanu metodu getPerimeter() i abstraktnu metodu getArea(). Možemo izračunati površinu svih elemenata na isti način pa smo implementovali tijelo getPerimeter() u Polygon. Sada svi poligoni koji implementiraju Polygon mogu koristiti getPerimeter() za izračunavanje površine. Međutim, pravilo za izračunavanje površine je različito za različite elemente. Dakle, getArea() je uključen bez implementacije. Svaka klasa koja implementuje Polygon mora osigurati implementaciju getArea().