Programowanie obiektowe. Wykład 03. Maciej Wołoszyn 17 marca Polimorfizm oraz wczesne i późne wiazanie

Podobne dokumenty
Dokumentacja do API Javy.

Klasy abstrakcyjne i interfejsy

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

Aplikacje w środowisku Java

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Java Język programowania

Programowanie obiektowe

Obszar statyczny dane dostępne w dowolnym momencie podczas pracy programu (wprowadzone słowem kluczowym static),

Wykład 8: klasy cz. 4

Wykład 7: Pakiety i Interfejsy

Interfejsy. Programowanie obiektowe. Paweł Rogaliński Instytut Informatyki, Automatyki i Robotyki Politechniki Wrocławskiej

C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie C++ - DZIEDZICZENIE.

PHP 5 język obiektowy

Kurs WWW. Paweł Rajba.

Materiały do zajęć VII

Polimorfizm. dr Jarosław Skaruz

Dziedziczenie. dr Jarosław Skaruz

Interfejsy i klasy wewnętrzne

Programowanie obiektowe i zdarzeniowe

Programowanie obiektowe w języku

1 Atrybuty i metody klasowe

Enkapsulacja, dziedziczenie, polimorfizm

Technologie i usługi internetowe cz. 2

Polimorfizm, metody wirtualne i klasy abstrakcyjne

Marcin Luckner Politechnika Warszawska Wydział Matematyki i Nauk Informacyjnych

Java: interfejsy i klasy wewnętrzne

Programowanie 2. Język C++. Wykład 3.

KLASY, INTERFEJSY, ITP

Kurs programowania. Wykład 1. Wojciech Macyna. 3 marca 2016

Wykład 9: Polimorfizm i klasy wirtualne

Informatyka I. Dziedziczenie. Nadpisanie metod. Klasy abstrakcyjne. Wskaźnik this. Metody i pola statyczne. dr inż. Andrzej Czerepicki

JAVA W SUPER EXPRESOWEJ PIGUŁCE

Klasy abstrakcyjne, interfejsy i polimorfizm

Języki i techniki programowania Ćwiczenia 3 Dziedziczenie

Programowanie obiektowe Wykład 6. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/14

Java: kilka brakujących szczegółów i uniwersalna nadklasa Object

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

PARADYGMATY PROGRAMOWANIA Wykład 2

Dziedziczenie. Tomasz Borzyszkowski

Programowanie obiektowe

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

Multimedia JAVA. Historia

Kurs programowania. Wykład 3. Wojciech Macyna. 22 marca 2019

Wykład 5 Okna MDI i SDI, dziedziczenie

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

Zaawansowane programowanie w C++ (PCP)

PARADYGMATY PROGRAMOWANIA Wykład 4

Programowanie współbieżne Wykład 8 Podstawy programowania obiektowego. Iwona Kochaoska

Podstawy Języka Java

Dziedziczenie jednobazowe, poliformizm

Kurs programowania. Wstęp - wykład 0. Wojciech Macyna. 22 lutego 2016

Programowanie obiektowe

dziedziczenie - po nazwie klasy wystąpią słowa: extends nazwa_superklasy

Kurs programowania. Wykład 13. Wojciech Macyna. 14 czerwiec 2017

Język Java część 2 (przykładowa aplikacja)

TEMAT : KLASY DZIEDZICZENIE

Klasy cd. Struktury Interfejsy Wyjątki

Programowanie obiektowe

C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy. C++ - klasy INNE SPOSOBY INICJALIZACJI SKŁADOWYCH OBIEKTU

Programowanie w Javie 1 Wykład i Ćwiczenia 3 Programowanie obiektowe w Javie cd. Płock, 16 października 2013 r.

Wykład 6: Dziedziczenie

Zaawansowane programowanie w języku C++ Klasy w C++

Język C++ Programowanie obiektowe

Wykład 5: Klasy cz. 3

Java podstawy jęyka. Wykład 2. Klasy abstrakcyjne, Interfejsy, Klasy wewnętrzne, Anonimowe klasy wewnętrzne.

WSNHiD, Programowanie 2 Lab. 2 Język Java struktura programu, dziedziczenie, abstrakcja, polimorfizm, interfejsy

Wykład 4: Klasy i Metody

Pakiety i interfejsy. Tomasz Borzyszkowski

Podstawy Programowania semestr drugi. Wykład czternasty

Pola i metody statyczne. Klasy zawierające pola i metody statyczne

Języki i metody programowania Java. Wykład 2 (część 2)

Podstawy programowania III

Wykład V. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

Przykład -

Dziedziczenie. Streszczenie Celem wykładu jest omówienie tematyki dziedziczenia klas. Czas wykładu 45 minut.

2. Klasy cz. 2 - Konstruktor kopiujący. Pola tworzone statycznie i dynamicznie - Funkcje zaprzyjaźnione - Składowe statyczne

Programowanie obiektowe - 1.


Programowanie obiektowe w C++ Wykład 12

Programowanie obiektowe, wykład nr 6. Klasy i obiekty

Języki i paradygmaty programowania Wykład 2. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/18

Diagram klas UML jest statycznym diagramem, przedstawiającym strukturę aplikacji bądź systemu w paradygmacie programowania obiektowego.

Instrukcja do pracowni specjalistycznej z przedmiotu. Obiektowe programowanie aplikacji

Klasy. dr Anna Łazińska, WMiI UŁ Podstawy języka Java 1 / 13

C++ - polimorfizm. C++ - polimorfizm. C++ - polimorfizm. C++ - polimorfizm. C++ - polimorfizm POLIMORFIZM

Dziedziczenie. Ogólna postać dziedziczenia klas:

Programowanie obiektowe

Instytut Mechaniki i Inżynierii Obliczeniowej Wydział Mechaniczny Technologiczny Politechnika Śląska

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM. Wykład 12

Programowanie w Internecie. Java

Definicje klas i obiektów. Tomasz Borzyszkowski

Wprowadzenie do programowanie obiektowego w języku C++

Zaawansowane programowanie w języku C++ Programowanie obiektowe

Co to jest klasa? Z programistycznego punktu widzenia klasa stanowi typ danych, który odwzorowuje wspólne cechy jakiegoś obiektu.

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

Programowanie obiektowe

Przypomnienie o klasach i obiektach

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

Transkrypt:

Programowanie obiektowe Wykład 03 Maciej Wołoszyn mailto:woloszyn@fatcat.ftj.agh.edu.pl 17 marca 2009 Spis treści 1 Polimorfizm oraz wczesne i późne wiazanie 1 1.1 Metody i klasy abstrakcyjne.......................... 3 1.2 Polimorfizm a tworzenie i usuwanie obiektów................. 5 2 Interfejsy 7 2.1 Implementacja wielu interfejsów........................ 8 2.2 Kolizje nazw.................................. 9 2.3 Dziedziczenie.................................. 10 2.4 Grupowanie stałych............................... 11 3 Klasy wewnętrzne 11 3.1 Anonimowe klasy wewnętrzne......................... 13 3.2 Klasy zagnieżdżone............................... 14 3.3 Tworzenie obiektów............................... 15 3.4 Dziedziczenie.................................. 15 3.5 Zastosowanie.................................. 16 1 Polimorfizm oraz wczesne i późne wiazanie wykorzystanie polimorfizmu wymaga mechanizmu tzw. późnego wiazania (late binding albo dynamic binding albo run-time binding) wybór uruchamianej metody nastę- Proszę o przesyłanie na ten adres informacji o znalezionych błędach, literówkach oraz propozycji zmian i uzupełnień. Dokument przygotowano za pomocą systemu LATEX. Wszelkie prawa zastrzeżone. 1

Programowanie obiektowe. Wykład 03 2 puje dopiero w trakcie działania programu możliwe dzięki rzutowaniu w górę (upcasting) czyli traktowaniu referencji do obiektu jako referencji do typu bazowego w większości przypadków metody w języku Java są uruchamiane z wykorzystaniem późnego wiazania nie dotyczy to jedynie metod typu static i final (przypomnienie: metody private w praktyce są również final) chociaż metody final są nieco efektywniejsze w działaniu (ze względu na wczesne wiązanie), to nie należy się przy ich tworzeniu kierować wydajnością, ale raczej organizacją hierarchii obiektów class Dane { void pokaz(){system.out.println("dane"); class Liczby extends Dane { void pokaz(){system.out.println("1 2"); class Litery extends Dane { void pokaz(){system.out.println("a B"); Dane d0,d1,d2; d0 = new Dane(); d1 = new Liczby(); d2 = new Litery(); d0.pokaz(); d1.pokaz(); d2.pokaz(); dane 1 2 A B class Dane { static void info(){ System.out.println("info:Dane");

Programowanie obiektowe. Wykład 03 3 class Liczby extends Dane { static void info(){ System.out.println("info:Liczby"); Dane d0,d1; d0 = new Dane(); d1 = new Liczby(); d0.info(); d1.info(); info:dane info:dane class Dane { private void test() { System.out.println("test:priv"); class Litery extends Dane { public void test() { System.out.println("test:publ"); Dane d2; d2 = new Litery(); // d2.test(); /* ZLE! */ ((Litery)d2).test(); // OK, ale... test:publ 1.1 Metody i klasy abstrakcyjne odpowiednik czysto wirtualnych funkcji w C++

Programowanie obiektowe. Wykład 03 4 umieszcza się jedynie deklarację takiej metody (poprzedzoną słowem kluczowym abstract), bez definicji; np. abstract String opis(); każda klasa zwierająca jedną lub więcej metod abstrakcyjnych jest klasą abstrakcyjną i musi być zdefiniowana z użyciem modyfikatora abstract abstract class Dane { static void info() { System.out.println("info:Dane"); abstract void pokaz(); class Liczby extends Dane { static void info() { System.out.println("info:Liczby"); void pokaz() { System.out.println("1 2"); Dane d0,d1,d2; // d0 = new Dane(); /* ZLE! */ d1 = new Liczby(); d1.pokaz(); Dane.info(); d1.info(); 1 2 info:dane info:dane klasa dziedzicząca po klasie abstrakcyjnej musi albo definiować wszystkie abstrakcyjne metody z klasy podstawowej, albo również pozostać abstrakcyjna // class DaneInne extends Dane { /* ZLE */ abstract class DaneInne extends Dane { nie wszystkie metody klasy abstrakcyjnej muszą być abstrakcyjne można zdefiniować klasę jako abstrakcyjną nawet jeśli nie zawiera żadnych abstrakcyjnych metod uniemożliwimy w ten sposób tworzenie obiektów danej klasy

Programowanie obiektowe. Wykład 03 5 1.2 Polimorfizm a tworzenie i usuwanie obiektów konstruktory są w zasadzie metodami typu static mogą być użyte podczas gdy obiekt jeszcze nie istnieje same nie będą więc wykazywać polimorfizmu kolejność inicjalizacji i uruchamiania konstruktorów jest następująca 1. uruchomienie konstruktora klasy bazowej (często rekursywnie!) 2. inicjalizacja pól w kolejności ich deklaracji 3. wykonanie ciała konstruktora klasy pochodnej najczęściej kwestię usuwania obiektów pozostawia się mechanizmowi garbage collector-a, ale jeśli klasa wykorzystująca mechanizmy dziedziczenia wymaga posprzątania po sobie, to oprócz napisania odpowiedniej metody należy zadbać, aby na koniec wywoływała ona również swojego odpowiednika z klasy bazowej to samo dotyczy uruchomienia metod sprzątających dla klas definiujących składniki naszego obiektu; kolejność powinna być odwrotna do kolejności inicjalizacji (np. kolejności deklaracji połączonych z inicjalizacją w definicji klasy) class Dane { void clr(){system.out.println("clr:dane"); class Litery extends Dane { Opis p = new Opis(); void clr(){ System.out.println("clr:Lit"); p.clr(); super.clr(); class Opis { void clr(){system.out.println("clr:opis"); System.out.println("Tworzymy obiekt..."); Dane d1 = new Litery(); System.out.println("Sprzatamy..."); d1.clr();

Programowanie obiektowe. Wykład 03 6 Tworzymy obiekt... Sprzatamy... clr:lit clr:opis clr:dane Wniosek: jeśli już potrzebujemy czegoś podobnego do destruktora z C++, to sami musimy zadbać o poprawną kolejność wykonywania poszczególnych operacji! (raczej niezbyt często się to zdarza) wywołanie z wnętrza konstruktora funkcji korzystających z późnego wiązania może doprowadzić do operowania na danych, które nie zostały jeszcze zainicjalizowane(!) np. gdy konstruktor klasy bazowej uruchomi funkcję, która zadziała dla obiektu potomnego zanim konstruktor klasy podstawowej zostanie uruchomiony, następuje tylko zainicjalizowanie przeznaczonej na obiekt pamięci zerami (odpowiedniego typu) abstract class Dane { abstract void pisz(); Dane() { System.out.println("Dane() BEGIN"); pisz(); // OSTROZNIE! System.out.println("Dane() END"); class Liczba extends Dane { int k=1; Liczba(int i) { System.out.println("Liczba(int) BEGIN"); k=i; pisz(); System.out.println("Liczba(int) END"); void pisz(){system.out.println("k="+k); Dane d1 = new Liczba(9); Dane() BEGIN k=0 Dane() END

Programowanie obiektowe. Wykład 03 7 Liczba(int) BEGIN k=9 Liczba(int) END kompilator w tym przypadku nie ostrzega o możliwym użyciu niezainicjalizowanych danych Wniosek: z konstruktora można bezpiecznie wywoływać tylko te metody, które są typu final w klasie podstawowej 2 Interfejsy są to całkowicie abstrakcyjne klasy, tzn. nie zawierające definicji żadnych metod, a jedynie same deklaracje mogą zawierać pola, ale będą one zawsze static i final definiują tylko formę, jaką ma przybrać każda klasa implementujaca interfejs określamy w ten sposób protokół, który ma zastosowanie do komunikacji z takimi klasami interfejsy definiuje się podobnie jak klasy, zastępując słowo class przez interface reguły dostępu do interfejsów, nazwy plików itp. pozostają takie same jak dla klas metody zadeklarowane w interfejsie są zawsze publiczne, nawet jeśli pominiemy słowo kluczowe public w klasach implementujących interfejs definiować musimy je więc również jako publiczne interface Pisze { void pisz(); // public! String s="[pisze]"; // static final! class Rzecz implements Pisze { //void pisz(){ /* ZLE! */ public void pisz() { System.out.println("..Rzecz.."); public static void opis(pisze x){ x.pisz();

Programowanie obiektowe. Wykład 03 8 Rzecz r = new Rzecz(); System.out.print("r.pisz() : "); r.pisz(); System.out.print("opis(r) : "); opis(r); System.out.println("r.s="+r.s); System.out.println("Pisze.s="+Pisze.s); r.pisz() :..Rzecz.. opis(r) :..Rzecz.. r.s=[pisze] Pisze.s=[Pisze] 2.1 Implementacja wielu interfejsów interfejsy pozwalają na konstrukcje przypominające wielokrotne dziedziczenie w C++ : można implementować w jednej klasie wiele interfejsów wystarczy po słowie kluczowym implements wymienić je oddzielone przecinkami interface ZwracaInt { int getint(); class Rzecz implements Pisze, ZwracaInt { public void pisz() { System.out.println("..Rzecz.."); public int getint() { return 99; public static void wartosc(zwracaint x){ System.out.println(".."+x.getInt()+".."); opis(r); wartosc(r);..rzecz....99..

Programowanie obiektowe. Wykład 03 9 równocześnie z implementacją nawet kilku interfejsów można dziedziczyć w zwykły sposób po innej klasie (abstrakcyjnej lub nie, ale tylko jednej!) dzięki temu możliwe jest wykorzystanie pochodzących z klasy bazowej implementacji metod wymaganych przez interfejs interface Liczy { void licz(); class MojaRzecz extends Rzecz implements Liczy { // implements Pisze // "odziedziczone" public void licz() { System.out.println("teraz licze.."); MojaRzecz m = new MojaRzecz(); m.licz(); teraz licze.. jeśli jakaś klasa ma być w zamierzeniu klasą bazową, to należy rozważyć zrealizowanie jej w postaci interfejsu klasa abstrakcyjna (lub ew. zwykła klasa) będzie korzystniejsza najczęściej tylko wtedy, jeśli musimy od razu w klasie bazowej zawrzeć definicje metod albo pola danych (za wyjątkiem static final) 2.2 Kolizje nazw należy zachować ostrożność, gdy równocześnie wykorzystujemy przeładowanie funkcji, dziedziczenie i interfejsy: nie można pozwolić, aby metody różniły się wyłacznie typem zwracanej wartości zaleca się unikanie identycznych nazw metod w interfejsach, które mogą być używane jednocześnie (w każdym razie należących do tego samego pakietu) interface IA { void f(); interface IB {

Programowanie obiektowe. Wykład 03 10 void f(int n); interface IC { int f(); /* OK - przeladowanie f() */ class CAB implements IA, IB { public void f() { public void f(int n) { /* ZLE - kolizja nazw! */ // class CAC implements IA, IC { // public void f() { // public int f() { return 0; // 2.3 Dziedziczenie mechanizm dziedziczenia może być wykorzystywany również bezpośrednio dla interfejsów tworzymy w ten sposób nowe interfejsy: można rozbudowywać interfejs tworząc wersję pochodną (dziedziczenie po interfejsie) oraz łączyć kilka interfejsów w jeden nowy (tylko w tym przypadku możliwe jest dziedziczenie po kilku elementach podstawowych, też interfejsach) interface Pisze { void pisz(); interface Liczy { void licz(); interface PiszeLepiej extends Pisze { void piszlepiej(); interface PiszeLiczy extends Pisze, Liczy {

Programowanie obiektowe. Wykład 03 11 2.4 Grupowanie stałych wygodne w interfejsie, ponieważ pola są zawsze static final (oraz public) można uzyskać efekt podobny do użycia enum w C/C++ interface Stale { double XA = 1.234, XB = 5.678; System.out.println("XA="+Stale.XA); XA=1.234 podobnie jak dla pól final w zwykłych klasach, stałe nie muszą być inicjalizowane wartościami znanymi w momencie kompilacji import java.util.*; interface LiczbaRnd { Random rand = new Random(); int randomint = rand.nextint(10); 3 Klasy wewnętrzne klasy zdefiniowane wewnątrz innych klas pozwala to grupować związane ze sobą klasy decydując o sposobie dostępu do nich nie jest to jednak to samo co zawieranie obiektów odniesienie się do klasy wewnętrznej spoza niestatycznych metod klasy zewnętrznej jest możliwe za pomocą konstrukcji klasazewnętrzna.klasawewnętrzna class Dane2 { class Liczba { int k=1; class Opis { String s="opis:"; void pokaz(){ Liczba l = new Liczba(); Opis o = new Opis(); System.out.println(o.s+l.k);

Programowanie obiektowe. Wykład 03 12 Dane2 d = new Dane2(); d.pokaz(); opis:1... i po dodaniu do klasy Dane2 metody wartosc: Liczba wartosc(int n){ Liczba w = new Liczba(); w.k = n; return w; Dane2.Liczba dl = d.wartosc(7); System.out.println(dl.k); 7 tylko klasy wewnętrzne można deklarować jako private lub protected mogą pozostawać ukryte, a równocześnie dawać się rzutować do implementowanego interfejsu interface DaneTxt { String txt(); class Dane3 { private class Opis implements DaneTxt { String s; Opis(String t){s=t; public String txt() { return s; public DaneTxt op(string s) { return new Opis(s); Dane3 d = new Dane3(); DaneTxt t = d.op("moje dane"); System.out.println(t.txt());

Programowanie obiektowe. Wykład 03 13 moje dane klasy wewnętrzne wykorzystywane są m.in. do obsługiwania zdarzeń w programach korzystających z graficznego interfejsu użytkownika mogą być umieszczane także wewnatrz metod, a nawet zakresów w celu: zaimplementowania interfejsu i zwrócenia referencji utworzenia pomocniczej klasy do rozwiązania skomplikowanego zagadnienia; klasa pomocnicza nie ma być jednak widoczna na zewnątrz 3.1 Anonimowe klasy wewnętrzne przydatne do jednorazowego tworzenia obiektów, bez definiowania osobnej, nazwanej klasy następujące wyrażenia zwrócą taki sam obiekt: return new Baza() { /*... */ ; class Nowa implements Baza { /*... */ return new Nowa(); bezpośrednie użycie w anonimowej klasie wewnętrznej obiektu spoza niej jest możliwe, tylko jeśli obiekt jest final (nie dotyczy to przekazania obiektu do konstruktora klasy nadrzędnej ale odbywa się to jeszcze nie we wnętrzu klasy) interface DaneTxt { String txt(); class Dane4 { public DaneTxt op(final String s) { return new DaneTxt() { private int i = 7; public String txt() { return s+i; ; // uwaga na srednik!

Programowanie obiektowe. Wykład 03 14 Dane4 d = new Dane4(); DaneTxt t = d.op("anonim nr "); System.out.println(t.txt()); anonim nr 7 nie można utworzyć konstruktora dla klasy anonimowej (nie ma ona nazwy!), ale można się posłużyć blokiem inicjalizacyjnym do osiągnięcia podobnego efektu class Dane5 { public DaneTxt op(final String s) { return new DaneTxt() { { System.out.println("(init)"); public String txt() { return s; ; Dane5 d = new Dane5(); DaneTxt t = d.op("anonim"); System.out.println(t.txt()); (init) anonim 3.2 Klasy zagnieżdżone klasy wewnętrzne mają dostęp do składników klasy zewnętrznej bez użycia dodatkowych kwalifikatorów odbywa się to poprzez ukrytą referencję generowaną automatycznie przez kompilator jeśli nie jest nam potrzebne takie połączenie, to można utworzyć klasę wewnętrzną typu static będzie ona tzw. klasa zagnieżdżona konsekwencje definicji jako static: do utworzenia obiektu klasy zagnieżdżonej nie potrzeba obiektu klasy zewnętrznej nie ma dostępu do niestatycznych obiektów klasy zewnętrznej w odróżnieniu od zwykłych klas wewnętrznych mogą zawierać dane statyczne można je zagnieździć wewnątrz interfejsu

Programowanie obiektowe. Wykład 03 15 3.3 Tworzenie obiektów do utworzenia obiektu klasy wewnętrznej potrzebne jest użycie już istniejacego obiektu klasy zewnętrznej class Zewn { class Wewn { Zewn z = new Zewn(); Zewn.Wewn w = z.new Wewn(); /*!!! */ ograniczenie to nie dotyczy klas zagnieżdżonych class Zewn { // class Wewn { /* teraz ZLE */ static class Wewn { Wewn() { System.out.println("Wewn()"); Zewn.Wewn w = new Zewn.Wewn(); Wewn() 3.4 Dziedziczenie dziedziczenie po klasie wewnętrznej wymaga zapewnienia, że zachowany jest związek klasy wewnętrznej z klasą zewnętrzną musi zostać przekazana do konstruktora referencja do klasy zewnętrznej i uruchomiona dla niej konstrukcja: referencjazewn.super(); brak takiej instrukcji w konstruktorze klasy potomnej spowoduje błąd kompilacji: an enclosing instance that contains Zewn.Wewn is required class P extends Zewn.Wewn { // P() { /* ZLE! */ P(Zewn z) { z.super(); /*!!! */

Programowanie obiektowe. Wykład 03 16 Zewn z = new Zewn(); P p = new P(z); także to zastrzeżenie nie dotyczy klas zagnieżdżonych class Zewn { static class Wewn { class P extends Zewn.Wewn { P() { /* teraz OK */ P p = new P(); 3.5 Zastosowanie próba realizacji wielokrotnego dziedziczenia po klasach (a nie tylko interfejsach!) każda z klas wewnętrznych może dziedziczyć po innej klasie! class A { /*... */ abstract class B { /*... */ class M extends A { B zwrocb() { return new B() {; static void weza(a a) { /*... */ static void wezb(b b) { /*... */ M m = new M(); weza(m); wezb(m.zwrocb());