Абстрактни класове и интерфейси. Полиморфизъм

Абстрактни класове

В темата за методи разбрахте, че съществуват и т.нар абстрактни методи. Това са методи, които съдържат само декларация, но нямат имплементация. Декларират се с ключовата дума abstract. Ако декларираме поне 1 такъв метод в рамките на даден клас, то този клас задължително трябва да съдържа в декларацията си думата abstract, тоест трябва да бъде абстрактен.

Определение: Абстрактен клас е такъв клас, който е деклариран с ключовата дума abstract и съдържащ или не абстрактни методи.

В един абстрактен клас може да напишем както абстрактни методи, така и такива които не са абстрактни– ще ги наричаме конкретни методи.

// Declaration using abstract keyword

public abstract class AbstractClassExample{

// Concrete method: body and braces

public void someMethod(){

//Statements here

}

// Abstract method: without body and braces

public abstract void firstaAbstractMethod();
}

 

Нека дадем по-конкретен пример:

public abstract class Figure {

public abstract double area(float a, float b);

}

class Triangle extends Figure{

@Override

 public double area(float a, float b) {

 return a*b/2;

 }

}

class Rectangle extends Figure{

@Override

 public double area(float a, float b) {

 return a*b;

 }

}

Важно!!!

  • Абстрактен клас не може да се инстанцира! Може само да се наследява.
  • Ако даден клас наследи абстрактен клас, то той задължително трябва да имплементира абстрактните му методи или и той трябва да е абстрактен, ако остави дори и един от тях абстрактен.

Защо са ни необходими абстрактните класове?

Абстрактните класове, както подсказва името им носят известно ниво абстракция. В йерархията на наследяването те могат да обобщят определена функционалност или белези, характерни за определено ниво е нея.
Пример:

Нека имаме клас Животно и няколко други класове – Котка, Куче, Кон. Последните три класа наследяват класа Животно, така че те имат няколко общи навици(методи), които имат всички животни, затова ги наследяват от Животно. Ако осмислите примера, ще разберете, че няма смисъл да създаваме инстанция на клас Животно, защото няма как да знаем какво е това животно. Затова спазваме следната философия в ООП: ако имаме клас, който да обобщи поведение и неспецифични, а общи белези на някои други класове, то го декларираме като абстрактен клас, а наследниците му – като конкретни класове, които задължително да имплементират поведението, но всеки по свой начин. Ако за Животно имаме метод(поведение) „издаване на звук“, то това поведение би било абстрактно, защото различните животни издават различни звуци. Например клас Куче ще имплементира функционалността „издаване на звук“ като „Бау-бау“, Котка – като „Мяу – мяу“ и тн. Ако създадем обект от тип Куче и извикаме метода му за издаване на звук, то ще лае, но ако създадем обект (хипотетично) от клас Животно и извикаме метода му за издаване на звук, то няма да знае какъв звук да издаде.

Интерфейси:

Ако имаме абстрактен клас, който съдържа само абстрактни методи, то спокойно можем да го направим на интерфейс. Ето и определение за интерфейс:

Интерфейсът е подобен на абстрактния клас, но с тази разлика, че може да съдържа абстрактни методи и член променливи. С навлизането на Java 8, в интерфейсите вече може да има както абстрактни, така и конкретни – имплементирани методи. Последните се наричат default methods – нововъведение, позволяващо да се задава функционалност по подразбиране. Променливите, декаларирани в интерфейса са само публични, статични и константни – public final static.

Деклариране на интерфейс: Става с ключовата дума interface:

public interface FirstInterface{ 
 /* All the methods are public abstract by default     Note down that these methods are not having body   */  
 public abstract void method1();   
 public abstract void method2();
}

За да може един клас да наследи интерфейса, то се използва думата implements. Ако един клас наследи (имплементира) интерфейс, то той задължително трябва да имплементира всички негови абстрактни методи, или да се декларира като абстрактен.

Примерна имплементация:

 

public interface MyInterface{
 public abstract void method1();  
 public abstract void method2();
}

public class XYZ implements MyInterface{ 
 public void method1() {     
  System.out.println("implementation of method1");
 } 

 public void method2() { 
    System.out.println("implementation of method2");
 } 

public static void main(String arg[]) {    
 MyInterface obj = new XYZ();    
 obj. method1();
 }
}

 

Важно!!!

  • Интерфейс не може да имплементира друг интерфейс, но може да го наследи ако се налага. Пример: Ако един интерфейс наследява друг, то ако създадем клас, който да имплементира наследника, то той ще трябва да имплементира всички методи и на двата интерфейса, но ако се имплементира родителския интерфейс, то в дадения клас трябва да се имплементират само неговите методи.
public interface Inf1{   
  public abstract void method1();
}

public interface Inf2 extends Inf1 {   
  public abstract void method2();
}

public class Demo implements Inf2{ 

  public void method1(){
    //Implementation of method1
  }

  public void method2(){ 
   //Implementation of method2
  }
}
  • Интерфейс не може да се инстанцира!
  • В интерфейс може да има абстрактни и default методи.
  • Интерфейс може да бъде имплементиран само от клас.
  • За да може клас да имплементира интерфейс, то интерфейсът трябва да е публичен, методите му също.
  • Интерфейсът не може да бъде деклариран с private, protected или transient.
  • По подразбиране всички методи на интерфейса са: public abstract.
  • Променливите на интерфейса са public static final Например:

interface Try{
public int b=10;
final int d=10;
static int e=0;

public static final int c=10;
int a=10;
}

Всички променливи горе са индентични.

  • Променливите на интерфейса трябва да бъдат инициализирани по време на декларацията, иначе настъпва грешка при компилиране.
  • В който и да е клас, имплементиращ интерфейс, не можете да променяте стойността на интерфейсна променлива, защото както споменахме по-горе по подразбиране са константни и статични.
  • Едни клас може да имплементира множество интерфейси! Това е много важно твърдение, защото благодарение на тази възможност Java реализира, макар и не напълно, множествено наследяване – тоест един клас би могъл да имплеменира множество поведения(интерфейси) едновременнно. Така се разрешава Deadly Diamond of Death (DDD) problem(прочетете повече за него!).
  • Ако има 2 еднакви метода в 2 различни интерфейса и даден клас ги имплементира и двата, то е необходимо само веднъж да се имплементират в класа.
  • Методи с еднакви сигнатури, но различни типове на връщан резултат не могат да бъдат имплементирани повече от веднъж, дори и да са от два различни интерфвейса и даденият клас да имлементира и двата интерфейса:
interface A{

  public abstract void aaa();

}

interface B{

  public abstract int aaa();

}

class Central implements A,B {


  public void aaa() { // error
 
  }

  public int aaa(){ /error

  }

}

 

  • Ако има конфликт по отношение на съвпадащи имена на променливи в различни итнтерфейси, имплементирани от един клас, то той се разрешава като те се достъпват по статичен начин с името на интерфейса.

 

Полиморфизъм

Полиморфизмът е един от четирите принципа на обектно-ориентираното програмиране. Понятието ‘полиморфизъм’ произлиза от биологията и означава ‘много форми’. От гледна точка на програмирането се интерпретира като „един интерфейс – много методи”.

Нека даден един пример:

Различните превозни средства (леки автомобили, тирове, автобуси) имат различно устройство на двигателя. Те обаче се управляват по един и същи начин и основната им функция е да извършват движение. Всички имат: волан, скоростна кутия, мигачи, фарове… Въпреки това, движението се извършва по различен начин поради различното устройство на превозното средство. Тоест, ако трябва да обобщим: Различните обекти извършват едно и също действие по различен начин (‘един интерфейс- много форми). Именно тази тяхна характеристика наричаме ‘полиморфизъм’.

Практически пример за полиморфизъм е и примерът, посочен по-горе за намиране на лице на фигура- площта се намира по различен начин в зависимост от това, коя е фигурата, но в крайна сметка търсената величина остава лицето.

Явор Томов,  Даниел Джолев

5 thoughts on “Абстрактни класове и интерфейси. Полиморфизъм”

  1. public abstract class Figure {

    static void printSides(Figure fig){
    if(fig instanceof Triangle){
    ((Triangle) fig).printSides();
    }
    else if(fig instanceof Okryjnost) {
    ((Okryjnost) fig).printRadius();
    }
    }
    }

    public class Triangle extends Figure{
    private Integer[] sides;

    public Triangle(Integer[] arr){

    this.sides=arr;
    }

    public void printSides(){
    for(int i=0;i<sides.length;i++){
    System.out.println(sides[i]);
    }
    }

    }

    public class Okryjnost extends Figure{

    private Integer radius;

    public Okryjnost(Integer rad){
    this.radius=rad;
    }

    public void printRadius(){
    System.out.println(this.radius);
    }
    }

    public class main {

    public static void main(String[] args) {
    // TODO Auto-generated method stub

    Integer[] arr={1,2,3};
    Figure tring = new Triangle(arr);
    Figure okr = new Okryjnost(5);

    Figure.printSides(okr);
    }

    }

  2. Тоест, пример за полиморфизъм би бил следният:
    – един интерфейс Figure с един абстрактен метод area();
    – един клас, който наследява този интерфейс и имплементира този метод по един начин – ако класът е Rectangle, имплементираме метода area() така, че да смята лице по формулата за лице на правоъгълник;
    – друг клас, който също наследява този интерфейс и имплементира неговия метод по друг начин – ако вторият клас е Triangle, имплементира метода area() така, че да смята лице на триъгълник;
    – на практика получаваме две имплементации на един и същи метод с едно и също име в два отделни класа.

    И когато създадем един обект от класа Rectangle (т.e. създаваме един правоъгълник) извикваме на този обект метода area() и смятаме лице на правоъгълник, когато създаваме обект от клас Triangle, извикваме на него area() и смятаме лице на триъгълник.

    Силно се надявам да съм го разбрал.

    1. Да, точно това е способността на един метод да “прави” различни неща(считай “има различна имплементация”) в зависимост от обекта, през който е извикан. На практика дефинираме един интерфейс (абстрактен метод area()) и реализираме най-различни имплементации на този интерфейс.

  3. Грешка ли е ако в интерфейса пишем в декларацията на метода abstract?

Leave a Reply

Your email address will not be published. Required fields are marked *