Dziedziczenie i polimorfizm

Dziedziczenie i polimorfizm to podstawowe mechanizmy programowania obiektowego. Przykłady i ćwiczenia z tego działu mają na celu zapoznanie ze składnią i koncepcjami stojącymi za dziedziczeniem i polimorfizmem. Wykorzystamy także nabyte umiejętności do zaprojektowania i zaimplementowania prostego przykładu, Twojego własnego pomysłu, wykorzystującego mechanizmy programowania obiektowego.

Przykład 3.1

Poniżej znajdziesz kod źródłowy klasy Prostokat, która dziedziczy po bibliotecznej klasie java.awt.Rectangle. Zapoznaj się z dokumentacją klasy java.awt.Rectangle i w oparciu o informacje z wykładu zidentyfikuj elementy składowe które klasa Prostokat odziedziczy po java.awt.Rectangle. Odpowiedz na pytanie co one reprezentują i/lub do czego służą.

import java.awt.Rectangle;

class Prostokat extends Rectangle
{
  Prostokat(int a,int b)
  {
    super(a,b);
  }
  void info()
  {
    System.out.println(this);
  }
}

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

    Prostokat a=new Prostokat(3,4);
    a.info();

    Prostokat b=new Prostokat(2,2);
    b.info();

    if(a.intersects(b))
    {
      System.out.println("-- przecinaja sie --\n");
    }
    else
    {
      System.out.println("-- NIE przecinaja sie --\n");
    }

    a.translate(5,3);
    a.info();

    if(a.intersects(b))
    {
      System.out.println("-- przecinaja sie --\n");
    }
    else
    {
      System.out.println("-- NIE przecinaja sie --\n");
    }
  }
}

Ćwiczenie 3.1.1

Przeanalizuj zawartość metody main() klasy Program. Zidentyfikuj miejsca w których utworzone zostały obiekty typu Prostokat oraz miejsca w których znajdują się wywołania metod na rzecz tych obiektów. Które z tych metod zostały zaimplementowane w klasie Prostokat, a które są odziedziczone po klasie java.awt.Rectangle? W oparciu o informacje zawarte w dokumentacji klasy java.awt.Rectangle odpowiedz na pytanie do czego służy każda z tych metod i w jakim celu została wykorzystana w powyższym przykładzie.

Ćwiczenie 3.1.2

Odpowiedz na pytanie czy konstruktory nadklasy są dziedziczone, a także co oznacza super() w wywołaniu konstruktora podklasy. W oparciu o informacje zawarte w dokumentacji klasy java.awt.Rectangle zaimplementuj konstruktor Prostokat(Point wierzcholek,int dlugosc,int szerokosc), gdzie wierzcholek będzie obiektem klasy java.awt.Point. Skompiluj i przetestuj przykład.

Ćwiczenie 3.1.3

W oparciu o informacje zawarte w dokumentacji klasy java.awt.Rectangle zaimplementuj w klasie Prostokat metodę sprawdzającą czy dany prostokąt przylega do innego prostokąta. Skompiluj i przetestuj przykład.

Przykład 2

Poniżej znajdziesz prosty przykład ilustrujący koncepcję i wykorzystanie polimorfizmu. Przykład składa się z abstrakcyjnej klasy bazowej Figura i kilku klas konkretnych zawierających implementacje metod obliczających pola i obwody figur geometrycznych.

abstract class Figura //nie mozna tworzyc instancji tej klasy
{
  abstract double pole(); //metoda abstrakcyjna
  abstract double obwod();

  void info()
  {
    System.out.println(this);
  }
}

class Okrag extends Figura
{
  double promien;

  Okrag(double promien)
  {
    this.promien=promien;
  }

  double pole()
  {
    return 3.14*promien*promien;
  }

  double obwod()
  {
    return 2*3.14*promien;
  }

  public String toString()
  {
    return "okrag o pr. "+promien;
  }
}

class Prostokat extends Figura
{
  double dlugosc;
  double szerokosc;

  Prostokat(double dlugosc,double szerokosc)
  {
    this.dlugosc=dlugosc;
    this.szerokosc=szerokosc;
  }

  double pole()
  {
    return dlugosc*szerokosc;
  }

  double obwod()
  {
    return 2*dlugosc+2*szerokosc;
  }

  public String toString()
  {
    return "prostokat o wym. "+dlugosc+" na "+szerokosc;
  }
}

public class Program
{
  public static void main(String[] args)
  {
    Figura z=new Okrag(2);
    z.info();

    Figura[] a={new Prostokat(3,5),new Okrag(8),new Okrag(3)};

    Figura x;
    double suma=0;

    for(int i=0;i<a.length;i++)
    {
      x=a[i];
      x.info();
      suma=suma+x.pole();
    }

    System.out.println("suma pol figur: "+suma);
  }
}

Ćwiczenie 3.2.1

Rozwiń powyższy przykład dodając klasy reprezentujące kilka innych figur geometrycznych. Zaimplementuj odpowiednie metody pozwalające na obliczanie pól i obwodów tych figur.

Przykład 3

Poniżej znajdziesz szkielet modelu bazy danych dokumentów, która ilustruje mechanizm wykorzystania interfejsów. Warto pamiętać że w języku Java klasy mogą implementować kilka interfejsów, inaczej niż w przypadku dziedziczenia.

class Osoba
{

}

interface Przeszukiwalne
{
  boolean czyPasuje(String wzorzec);
}

abstract class Dokument implements Przeszukiwalne
{

}

class Paszport extends Dokument
{
  public boolean czyPasuje(String wzorzec)
  {
    return false;
  }

  public String toString()
  {
    return "";
  }
}

class DowodOsobisty extends Dokument
{
  public boolean czyPasuje(String wzorzec)
  {
    return false;
  }
  public String toString()
  {
    return "";
  }
}

public class Program
{
  public static void main(String[] args)
  {
    Dokument[] bazaDanych={new Paszport(),new DowodOsobisty(),new Paszport()};
    Dokument z;
    String wzorzec="Gorniak";
    for(int i=0;i<bazaDanych.length;i++)
    {
      z=bazaDanych[i];
      if(z.czyPasuje(wzorzec))System.out.println("znaleziono: "+z);
    }
  }
}

Ćwiczenie 3.3.1

Rozwiń powyższy przykład. Dodaj odpowiednie pola i zaimplementuj odpowiednie metody, żeby uzyskać model bazy danych dokumentów, realizujący wyszukiwanie według podanego wzorca. Uwaga: porównanie zawartości dwóch obiektów typu String można zrealizować np. za pomocą wywołania funkcji equalsIgnoreCase(String anotherString) z klasy java.lang.String.

Przykład 4

Poniżej znajdziesz przykład ilustrujący serializację obiektów do strumienia danych z wykorzystaniem interfejsu Serializable wchodzącego w skład Java Platform API.

import java.io.*;

class Osoba implements Serializable
{
  String imie;
  String nazwisko;
  int rokUrodzenia;

  Osoba(String imie,String nazwisko,int rokUrodzenia)
  {
    this.imie=imie;
    this.nazwisko=nazwisko;
    this.rokUrodzenia=rokUrodzenia;
  }

  Osoba(BufferedReader br)
  {
    try
    {
      System.out.print("imie: ");
      this.imie=br.readLine();

      System.out.print("nazwisko: ");
      this.nazwisko=br.readLine();

      System.out.print("rok urodzenia: ");
      this.rokUrodzenia=Integer.parseInt(br.readLine());
    }
    catch(IOException e){}
  }

  public String toString()
  {
    return this.imie+" "+this.nazwisko+" "+this.rokUrodzenia;
  }
}

class DowodOsobisty implements Serializable
{
  Osoba posiadacz;
  String numer;

  DowodOsobisty(BufferedReader br)
  {
    try
    {
      this.posiadacz=new Osoba(br);
      System.out.print("numer do: ");
      this.numer=br.readLine();
    }
    catch(IOException e){}
  }

  public String toString()
  {
    return "<do:> "+posiadacz.toString()+" "+this.numer;
  }

  void info()
  {
    System.out.println(this);
  }
}

public class Program
{
  public static void main(String[] args)
  {
    System.out.println("-- do zapisu --");
    BufferedReader br=new BufferedReader(new InputStreamReader(System.in));

    DowodOsobisty z=new DowodOsobisty(br);
    z.info();

    try
    {
      ObjectOutputStream outp=new ObjectOutputStream(new
                                  FileOutputStream("plik.dat"));
      outp.writeObject(z);
      outp.close();
    }
    catch(Exception e){System.out.println(e);}

    System.out.println("\n-- z pliku --");
    ObjectInputStream inp;

    try
    {
      inp=new ObjectInputStream(new FileInputStream("plik.dat"));
      Object o=inp.readObject();
      DowodOsobisty x=(DowodOsobisty)o;
      inp.close();
      x.info();
    }
    catch(Exception e){System.out.println(e);}
  }
}

Ćwiczenie 3.4.1

Rozwiń powyższy przykład. Zidentyfikuj i obsłuż wyjątek mający miejsce w sytuacji kiedy nie można otworzyć pliku o podanej nazwie.

Następna część - Wstęp