Czy możemy utworzyć instancję klasy abstrakcyjnej?


573

Podczas jednego z moich wywiadów zapytano mnie: „Czy możemy stworzyć instancję abstrakcyjną?”

Moja odpowiedź brzmiała: „Nie, nie możemy”. Ale ankieter powiedział mi: „Źle, możemy”.

Trochę się o to spierałem. Potem kazał mi sam to wypróbować w domu.

abstract class my {
    public void mymethod() {
        System.out.print("Abstract");
    }
}

class poly {
    public static void main(String a[]) {
        my m = new my() {};
        m.mymethod();
    }
}

Tutaj tworzę instancję mojej klasy i wywołuję metodę klasy abstrakcyjnej. Czy ktoś może mi to wyjaśnić? Czy naprawdę się myliłem podczas mojego wywiadu?


2
Chociaż tylko nieco podobne, można chyba instancję klasy abstrakcyjnej w C ++: jeśli wywodzą się spoza klasy abstrakcyjnej Bz abstrakcyjnego jeden A, w trakcie części konstrukcji z Binstancji, który składa się z systemem A„s konstruktora typu wykonawcze obiektu jest faktycznie A. Jednak tylko tymczasowo.
Vlad

8
@jWeavers: Podany przez niego przykład jest całkowicie błędny. Powinieneś był zapytać od niego „jaki jest pożytek z klasy abstrakcyjnej”. Jeśli go rozszerzasz, to dlaczego tworzysz instancję klasy rozszerzonej? Jest to zupełnie nowy obiekt, w którym kończy się brak danych ..
Sok z cytryny

3
A może rozmówca chciał sprawdzić, czy jesteś pewny swojego oświadczenia w stosunku do tego, co zaproponował!
Sid

5
Okłamał cię. Upuściłeś piłkę, gdy nie zauważyłeś, że nie robi tego kod, i wyjaśnisz, czym są anonimowe podklasy. Prawdopodobnie już to wiedział i chciał sprawdzić, czy wiesz.
candied_orange

2
To nie był quiz, ale rozmowa kwalifikacyjna, prawda? A co jeśli Java lub C ++ pozwoliły na tworzenie klas abstrakcyjnych? Nie zrobiłbyś tego, ponieważ nie jest to mądre. W Objective-C klasy abstrakcyjne są abstrakcyjne tylko zgodnie z konwencją, a ich tworzenie jest błędem.
gnasher729,

Odpowiedzi:


722

Tutaj tworzę instancję mojej klasy

Nie, nie tworzysz tutaj instancji swojej klasy abstrakcyjnej. Zamiast tego tworzysz instancję anonimowej podklasy swojej klasy abstrakcyjnej. Następnie wywołujesz metodę z referencyjnego odwołania do klasy, wskazując obiekt podklasy .

To zachowanie jest wyraźnie wymienione w JLS - sekcja # 15.9.1 : -

Jeśli wyrażenie tworzenia instancji klasy kończy się na treści klasy, instancja klasy jest klasą anonimową. Następnie:

  • Jeśli T oznacza klasę, deklarowana jest anonimowa bezpośrednia podklasa klasy o nazwie T. Jest to błąd czasu kompilacji, jeśli klasa oznaczona jako T jest klasą końcową.
  • Jeśli T oznacza interfejs, wówczas deklarowana jest anonimowa bezpośrednia podklasa Object, która implementuje interfejs nazwany przez T.
  • W obu przypadkach treścią podklasy jest ClassBody podane w wyrażeniu tworzenia instancji klasy.
  • Instancja klasy jest anonimową podklasą.

Podkreśl moje.

Ponadto w JLS - sekcja # 12.5 można przeczytać o procesie tworzenia obiektów . Zacytuję tutaj jedno stwierdzenie: -

Za każdym razem, gdy tworzona jest nowa instancja klasy, przydzielana jest dla niej przestrzeń pamięci z miejscem na wszystkie zmienne instancji zadeklarowane w typie klasy i wszystkie zmienne instancji zadeklarowane w każdej nadklasie typu klasy, w tym wszystkie zmienne instancji, które mogą być ukryte.

Tuż przed zwróceniem odwołania do nowo utworzonego obiektu wskazany konstruktor jest przetwarzany w celu zainicjowania nowego obiektu przy użyciu następującej procedury:

O pełnej procedurze możesz przeczytać na podanym przeze mnie linku.


Aby praktycznie zobaczyć, że instancja klasy jest anonimową podklasą , wystarczy skompilować obie klasy. Załóżmy, że umieścisz te klasy w dwóch różnych plikach:

My.java:

abstract class My {
    public void myMethod() {
        System.out.print("Abstract");
    }
}

Poly.java:

class Poly extends My {
    public static void main(String a[]) {
        My m = new My() {};
        m.myMethod();
    }
}

Teraz skompiluj oba pliki źródłowe:

javac My.java Poly.java

Teraz w katalogu, w którym skompilowałeś kod źródłowy, zobaczysz następujące pliki klas:

My.class
Poly$1.class  // Class file corresponding to anonymous subclass
Poly.class

Zobacz tę klasę Poly$1.class. Jest to plik klasy utworzony przez kompilator odpowiadający anonimowej podklasie utworzonej przy użyciu poniższego kodu:

new My() {};

Jest więc jasne, że tworzy się inną klasę. Tyle, że ta klasa otrzymuje nazwę dopiero po kompilacji przez kompilator.

Ogólnie rzecz biorąc, wszystkie anonimowe podklasy w twojej klasie zostaną nazwane w następujący sposób:

Poly$1.class, Poly$2.class, Poly$3.class, ... so on

Liczby te oznaczają kolejność, w jakiej te anonimowe klasy pojawiają się w klasie zamykającej.


172
@coders. Dokładna odpowiedź brzmi: - Nie możesz utworzyć instancji swojej klasy abstrakcyjnej, ale możesz utworzyć konkretną podklasę swojej klasy abstrakcyjnej.
Rohit Jain

16
W jednym wierszu możesz powiedzieć: - Nigdy nie można utworzyć instancji klasy abstrakcyjnej. Taki jest cel klasy abstrakcyjnej.
Rahul Tripathi

66
Wygląda na to, że ankieter był bardziej zainwestowany w jego odpowiedź niż w twoją ...
Neil T.

7
Według innego komentarza (z odniesieniem do JLS ): „Mówi się, że obiekt jest instancją swojej klasy i wszystkich nadklas swojej klasy” - czy zatem nie tworzymy tutaj technicznie instancji klasy abstrakcyjnej? tj. utworzenie instancji klasy abstrakcyjnej?
arshajii

6
@ARS Powiedziałbym, że jest różnica między byciem instance ofa instantiating. Tworzysz tylko jedną klasę, a tworzony obiekt może być instancją wielu klas z powodu dziedziczenia.
Simon Forsberg

89

Powyższe tworzy instancję anonimowej klasy wewnętrznej, która jest podklasą myklasy abstrakcyjnej. Nie jest to jednoznacznie odpowiednik tworzenia instancji samej klasy abstrakcyjnej. OTOH, każda instancja podklasy jest instancją wszystkich jej superklas i interfejsów, więc większość klas abstrakcyjnych jest rzeczywiście tworzona przez utworzenie jednej z ich konkretnych podklas.

Jeśli osoba przeprowadzająca wywiad powiedziała „źle!” bez wyjaśnienia i podając ten przykład, jako wyjątkowy kontrprzykład, myślę, że nie wie o czym mówi.


10
Ściśle mówiąc, abstrakcyjna nadklasa nie jest tworzona. Wywoływany jest konstruktor w celu inicjalizacji zmiennych instancji.
Percepcja

4
Tak, to: subclassInstance instanceof SuperClasszwróci wartość true, więc obiekt jest instancją nadklasy, co oznacza, że ​​nadklasa została zaszczepiona. Ale to tylko semantyczne nitpicking.
JB Nizet,

5
To może być semantyka. Java definiuje tworzenie instancji pod względem tworzenia obiektów za pomocą nowego słowa kluczowego (czego nie można zrobić z klasą abstrakcyjną). Ale oczywiście konkretna podklasa prawidłowo zgłasza, że ​​jest to instancja każdego członka macierzystej hierarchii.
Percepcja

11
pkt 4.12.6 JLS mówi: „Mówi się, że obiekt jest instancją swojej klasy i wszystkich nadklas swojej klasy”.
JB Nizet,

85

= my() {};oznacza, że jest anonimowy realizacji, nie jest proste instancji obiektu, który powinien być: = my(). Nigdy nie można utworzyć instancji klasy abstrakcyjnej.


30

Tylko obserwacje, które możesz zrobić:

  1. Dlaczego się polyrozszerza my? To jest bezużyteczne ...
  2. Jaki jest wynik kompilacji? Trzy pliki: my.class, poly.classipoly$1.class
  3. Jeśli potrafimy utworzyć taką klasę abstrakcyjną, możemy też utworzyć interfejs również ... dziwne ...


Czy możemy utworzyć instancję klasy abstrakcyjnej?

Nie możemy. Możemy stworzyć anonimową klasę (to trzeci plik) i utworzyć ją.


Co o super klasy instancji?

Abstrakcyjna superklasa nie jest tworzona przez nas, ale przez java.

EDYCJA: Poproś go, aby to przetestował

public static final void main(final String[] args) {
    final my m1 = new my() {
    };
    final my m2 = new my() {
    };
    System.out.println(m1 == m2);

    System.out.println(m1.getClass().toString());
    System.out.println(m2.getClass().toString());

}

Wyjście jest:

false
class my$1
class my$2

+1 do obserwacji 3: przykład możemy zrobić Serializable s = new Serializable() {};(co jest całkiem bezużyteczny) i jeśli oznaczone na kod dałoby class my$3(lub cokolwiek załączając klasę i numer)
dozbrojenie Monica - notmaynard

18

Możesz po prostu odpowiedzieć w jednym wierszu

Nie , nigdy nie możesz instancji klasy abstrakcyjnej

Ale ankieter nadal nie zgadza się, wtedy możesz mu / jej powiedzieć

wszystko, co możesz zrobić, to stworzyć Anonimową Klasę.

I, zgodnie z klasą Anonimową, klasa zadeklarowała i utworzyła instancję w tym samym miejscu / linii

Możliwe więc, że osoba przeprowadzająca wywiad byłaby zainteresowana sprawdzeniem poziomu pewności siebie i tego, ile wiesz o operacjach operacyjnych.


17

Część techniczna została dobrze omówiona w innych odpowiedziach, a kończy się głównie na:
„Myli się, nie wie rzeczy, poproś go, aby przyłączył się do SO i wszystko wyjaśnił :)”

Chciałbym odnieść się do faktu (wspomnianego w innych odpowiedziach), że może to być stresujące pytanie i jest ważnym narzędziem dla wielu ankieterów, aby dowiedzieć się więcej o tobie i jak reagujesz na trudne i nietypowe sytuacje. Podając ci nieprawidłowe kody, prawdopodobnie chciał sprawdzić, czy się odezwałeś. Aby dowiedzieć się, czy masz pewność, że przeciwstawisz się swoim seniorom w podobnych sytuacjach.

PS: Nie wiem dlaczego, ale mam wrażenie, że osoba przeprowadzająca wywiad przeczytała ten post.


13

Klasy abstrakcyjne nie mogą być tworzone, ale można je podklasować. Zobacz ten link

Najlepszym tego przykładem jest

Chociaż klasa Calender ma abstrakcyjną metodę getInstance () , ale kiedy mówiszCalendar calc=Calendar.getInstance();

calc odnosi się do instancji klasy klasy GregorianCalendar, ponieważ „GregorianCalendar rozszerza Kalendarz

Rzeczywiście annonymous typ wewnętrzna pozwala na stworzenie no-name podklasę klasy abstrakcyjnej i instancję tego.


11

Odpowiedź techniczna

Klasy abstrakcyjne nie mogą być tworzone - z definicji i projektu.

Z JLS, rozdział 8. Klasy:

Nazwana klasa może zostać uznana za abstrakcyjną (§ 8.1.1.1) i musi zostać zadeklarowana jako abstrakcyjna, jeśli nie została w pełni zaimplementowana; takiej klasy nie można utworzyć, ale można ją rozszerzyć o podklasy.

Z JSE 6 java doc dla Classes.newInstance ():

InstantiationException - jeśli ta klasa reprezentuje klasę abstrakcyjną, interfejs, klasę tablicową, typ pierwotny lub void; lub jeśli klasa nie ma zerowego konstruktora; lub jeśli instancja nie powiedzie się z innego powodu.

Można oczywiście utworzyć konkretną podklasę klasy abstrakcyjnej (w tym anonimową podklasę), a także przeprowadzić rzut typu odwołania do obiektu na typ abstrakcyjny.

A Different Angle Na To - Teamplay & Social Intelligence:

Tego rodzaju nieporozumienia technicznej często zdarza się w prawdziwym świecie, gdy mamy do czynienia z skomplikowanych technologii i specyfikacji legalistycznych.

„Umiejętności ludzkie” mogą być tutaj ważniejsze niż „umiejętności techniczne”. Jeśli rywalizujesz i agresywnie próbujesz udowodnić swoją stronę argumentu, możesz teoretycznie mieć rację, ale możesz także zrobić więcej obrażeń, walcząc / niszcząc „twarz” / tworząc wroga, niż jest to warte. Bądź pojednawczy i wyrozumiały w rozwiązywaniu różnic. Kto wie - może „masz rację”, ale pracujesz nad nieco innym znaczeniem terminów?

Kto wie - choć mało prawdopodobne, to możliwe, że osoba przeprowadzająca wywiad celowo wprowadziła mały konflikt / nieporozumienie, aby postawić cię w trudnej sytuacji i zobaczyć, jak zachowujesz się emocjonalnie i społecznie. Bądź uprzejmy i konstruktywny w stosunku do współpracowników, postępuj zgodnie z radami seniorów i postępuj zgodnie z wywiadem, aby rozwiązać wszelkie problemy / nieporozumienia - za pośrednictwem poczty elektronicznej lub telefonu. Pokazy jesteś zmotywowany i zorientowane na szczegóły.


7

Jest dobrze znanym faktem, że abstract classmoże nie być instancja jak wszyscy odpowiedzieli.

Gdy program definiuje anonimową klasę, kompilator faktycznie tworzy nową klasę o innej nazwie (ma wzór, w EnclosedClassName$nktórym njest anonimowy numer klasy)

Więc jeśli zdekompilujesz tę klasę Java, znajdziesz kod jak poniżej:

moja klasa

abstract class my { 
    public void mymethod() 
    { 
        System.out.print("Abstract"); 
    }
} 

poly $ 1.class (wygenerowana klasa „anonimowej klasy”)

class poly$1 extends my 
{
} 

ploly.cass

public class poly extends my
{
    public static void main(String[] a)
    {
        my m = new poly.1(); // instance of poly.1 class NOT the abstract my class

        m.mymethod();
    }
}

4

Nie, nie można utworzyć instancji klasy abstrakcyjnej, tworzymy tylko klasę anonimową. W klasie abstrakcyjnej deklarujemy metody abstrakcyjne i definiujemy tylko konkretne metody.


4

O klasach abstrakcyjnych

  • Nie można utworzyć obiektu klasy abstrakcyjnej
  • Może tworzyć zmienne (może zachowywać się jak typy danych)
  • Jeśli dziecko nie może zastąpić co najmniej jednej abstrakcyjnej metody rodzica, wówczas dziecko również staje się abstrakcyjne
  • Klasy abstrakcyjne są bezużyteczne bez klas podrzędnych

Celem klasy abstrakcyjnej jest zachowanie się jak baza. W hierarchii dziedziczenia zobaczysz klasy abstrakcyjne u góry.


3

Można powiedzieć:
nie możemy utworzyć instancji klasy abstrakcyjnej, ale możemy użyć newsłowa kluczowego, aby utworzyć anonimową instancję klasy, po prostu dodając {}jako implementację treści na końcu klasy abstrakcyjnej.


3

Rozszerzenie klasy nie oznacza, że ​​tworzysz instancję klasy. Właściwie w twoim przypadku tworzysz instancję podklasy.

Jestem całkiem pewien, że klasy abstrakcyjne nie pozwalają na inicjowanie. Powiedziałbym więc „nie”: nie można utworzyć instancji klasy abstrakcyjnej. Ale możesz go rozszerzyć / odziedziczyć.

Nie można bezpośrednio utworzyć instancji klasy abstrakcyjnej. Ale to nie znaczy, że pośrednio nie można uzyskać instancji klasy (a nie instancji oryginalnej klasy abstrakcyjnej). Mam na myśli, że nie możesz utworzyć oryginalnej klasy abstrakcyjnej, ale możesz:

  1. Utwórz pustą klasę
  2. Dziedzicz ją od klasy abstrakcyjnej
  3. Utwórz instancję klasy derwiści

Dzięki temu uzyskujesz dostęp do wszystkich metod i właściwości w klasie abstrakcyjnej za pośrednictwem instancji klasy pochodnej.


2

Nie można utworzyć instancji klasy abstrakcyjnej. To, co naprawdę możesz zrobić, to zaimplementować niektóre typowe metody w klasie abstrakcyjnej i pozwolić innym nie zaimplementować (uznając je za abstrakcyjne) i pozwolić konkretnemu zstępującemu zaimplementować je w zależności od ich potrzeb. Następnie możesz stworzyć fabrykę, która zwraca instancję tej abstrakcyjnej klasy (właściwie jego implementatora). Następnie w fabryce decydujesz, który dostawca wybrać. Jest to znane jako wzór fabryczny:

   public abstract class AbstractGridManager {
        private LifecicleAlgorithmIntrface lifecicleAlgorithm;
        // ... more private fields

        //Method implemented in concrete Manager implementors 
        abstract public Grid initGrid();

        //Methods common to all implementors
        public Grid calculateNextLifecicle(Grid grid){
            return this.getLifecicleAlgorithm().calculateNextLifecicle(grid);
        }

        public LifecicleAlgorithmIntrface getLifecicleAlgorithm() {
            return lifecicleAlgorithm;
        }
        public void setLifecicleAlgorithm(LifecicleAlgorithmIntrface lifecicleAlgorithm) {
            this.lifecicleAlgorithm = lifecicleAlgorithm;
        }
        // ... more common logic and getters-setters pairs
    }

Konkretny implementator musi tylko zaimplementować metody zadeklarowane jako abstrakcyjne, ale będzie miał dostęp do logiki zaimplementowanej w tych klasach w klasie abstrakcyjnej, które nie są zadeklarowane jako abstrakcyjne:

public class FileInputGridManager extends AbstractGridManager {

private String filePath;

//Method implemented in concrete Manager implementors 
abstract public Grid initGrid();

public class FileInputGridManager extends AbstractGridManager {

    private String filePath;

    //Method implemented in concrete Manager implementors 
    abstract public Grid initGrid();

    public Grid initGrid(String filePath) {
        List<Cell> cells = new ArrayList<>();
        char[] chars;
        File file = new File(filePath); // for example foo.txt
        // ... more logic
        return grid;
    }
}

Wreszcie fabryka wygląda mniej więcej tak:

public class GridManagerFactory {
    public static AbstractGridManager getGridManager(LifecicleAlgorithmIntrface lifecicleAlgorithm, String... args){
        AbstractGridManager manager = null;

        // input from the command line
        if(args.length == 2){
            CommandLineGridManager clManager = new CommandLineGridManager();
            clManager.setWidth(Integer.parseInt(args[0]));
            clManager.setHeight(Integer.parseInt(args[1]));
            // possibly more configuration logic
            ...
            manager = clManager;
        } 
        // input from the file
        else if(args.length == 1){
            FileInputGridManager fiManager = new FileInputGridManager();
            fiManager.setFilePath(args[0]);
            // possibly more method calls from abstract class
            ...
            manager = fiManager ;
        }
        //... more possible concrete implementors
        else{
            manager = new CommandLineGridManager();
        }
        manager.setLifecicleAlgorithm(lifecicleAlgorithm);
        return manager;
    }
}

Odbiorca AbstractGridManager wywoływałby na nim metody i uzyskałby logikę, zaimplementowaną w konkretnym descendencie (i częściowo w metodach klasy abstrakcyjnej), nie wiedząc, jaką konkretną implementację otrzymał. Jest to również znane jako inwersja kontroli lub wstrzykiwanie zależności.


2

Nie, nie możemy utworzyć obiektu klasy abstrakcyjnej, ale stworzyć zmienną referencyjną klasy abstrakcyjnej. Zmienna referencyjna służy do odwoływania się do obiektów klas pochodnych (podklasy klasy Abstract)

Oto przykład ilustrujący tę koncepcję

abstract class Figure { 

    double dim1; 

    double dim2; 

    Figure(double a, double b) { 

        dim1 = a; 

        dim2 = b; 

    } 

    // area is now an abstract method 

    abstract double area(); 

    }


    class Rectangle extends Figure { 
        Rectangle(double a, double b) { 
        super(a, b); 
    } 
    // override area for rectangle 
    double area() { 
        System.out.println("Inside Area for Rectangle."); 
        return dim1 * dim2; 
    } 
}

class Triangle extends Figure { 
    Triangle(double a, double b) { 
        super(a, b); 
    } 
    // override area for right triangle 
    double area() { 
        System.out.println("Inside Area for Triangle."); 
        return dim1 * dim2 / 2; 
    } 
}

class AbstractAreas { 
    public static void main(String args[]) { 
        // Figure f = new Figure(10, 10); // illegal now 
        Rectangle r = new Rectangle(9, 5); 
        Triangle t = new Triangle(10, 8); 
        Figure figref; // this is OK, no object is created 
        figref = r; 
        System.out.println("Area is " + figref.area()); 
        figref = t; 
        System.out.println("Area is " + figref.area()); 
    } 
}

Widzimy tutaj, że nie możemy utworzyć obiektu typu Figure, ale możemy utworzyć zmienną odniesienia typu Figure. Tutaj stworzyliśmy zmienną odniesienia typu Rysunek i Rysunek Zmienna odniesienia klasy służy do odniesienia do obiektów klasy Prostokąt i Trójkąt.


0

W rzeczywistości nie możemy bezpośrednio stworzyć obiektu klasy abstrakcyjnej. To, co tworzymy, jest zmienną referencyjną wywołania abstrakcyjnego. Zmienna referencyjna służy do odwoływania się do obiektu klasy, który dziedziczy klasę abstrakcyjną, tj. Podklasę klasy abstrakcyjnej.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.