Чи можемо ми створити абстрактний клас?


573

Під час одного з моїх інтерв'ю мене запитали "Чи можемо ми створити абстрактний клас?"

Моя відповідь була "Ні. Ми не можемо". Але інтерв'юер сказав мені: "Неправильно, ми можемо".

Я на цьому трохи сперечався. Тоді він сказав мені спробувати це вдома.

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

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

Тут я створюю екземпляр свого класу та називаю метод абстрактного класу. Хто-небудь може мені пояснити це? Чи справді я помилявся під час свого інтерв'ю?


2
Хоча тільки трохи пов'язаний, один , можливо , може створити екземпляр абстрактного класу в C ++: якщо ви черпали-абстрактний клас Bвід абстрактного A, в протягом частини будівництва в Bразі, полягає працює Aконструктор «s, тип виконання об'єкта насправді A. Однак лише тимчасові.
Влад

8
@jWeavers: Приклад, який він наводив, абсолютно невірний. Ви мусили запитати "тоді в чому користь абстрактного класу". Якщо ви розширюєте його, то чому ви створюєте екземпляр розширеного класу? Це абсолютно новий об’єкт, де ви закінчуєтесь без даних ..
Лимонний сік

3
Або інтерв'юер хотів би перевірити, наскільки ви впевнені у своїй заяві проти того, що він запропонував!
Сід

5
Він збрехав тобі. Ви кинули кулю, коли не змогли вказати, що це не те, що робить цей код, і пояснити, що таке анонімні підкласи. Він, мабуть, це вже знав і хотів побачити, чи знаєте ви.
candied_orange

2
Це не шоу вікторини, а співбесіда на роботі, правда? Що робити, якщо Java або C ++ дозволили створювати абстрактні класи? Ви б цього не зробили, бо це не розумно робити. У Objective-C абстрактні класи є абстрактними лише за умовами, і інстанціювати їх - помилка.
gnasher729

Відповіді:


722

Тут я створюю екземпляр свого класу

Ні, ви не створюєте тут екземпляр свого абстрактного класу. Швидше ви створюєте екземпляр анонімного підкласу вашого абстрактного класу. А потім ви посилаєтесь на метод у вашому абстрактному довіднику класу, що вказує на об’єкт підкласу .

Така поведінка чітко вказана в JLS - Розділ № 15.9.1 : -

Якщо вираз створення екземпляра класу закінчується в тілі класу, то інстанціюваний клас - це анонімний клас. Тоді:

  • Якщо T позначає клас, то оголошується анонімний прямий підклас класу, названого T. Це помилка часу компіляції, якщо клас, позначений T, є кінцевим класом.
  • Якщо T позначає інтерфейс, то оголошується анонімний прямий підклас Object, який реалізує інтерфейс, названий T.
  • У будь-якому випадку тілом підкласу є ClassBody, що задається у виразі створення екземпляра класу.
  • Клас, який інстанціюється, є анонімним підкласом.

Наголос мій.

Також у розділі №12.5 JLS ви можете прочитати про процес створення об’єктів . Я цитую тут одне твердження: -

Щоразу, коли створюється новий екземпляр класу, для нього виділяється простір пам’яті з простором для всіх змінних екземплярів, оголошених у класі класу, та всіх змінних екземплярів, оголошених у кожному надкласі типу класу, включаючи всі змінні екземпляра, які можуть бути приховані.

Незадовго до повернення в результаті посилання на щойно створений об'єкт, зазначений конструктор обробляється для ініціалізації нового об'єкта за допомогою наступної процедури:

Про повну процедуру ви можете прочитати за посиланням, яке я надав.


Щоб практично побачити, що клас, який інстанціюється, є Анонімним підкласом , вам просто потрібно скласти обидва класи. Припустимо, ви кладете ці класи у два різні файли:

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();
    }
}

Тепер, компілюйте обидва вихідні файли:

javac My.java Poly.java

Тепер у каталозі, де ви склали вихідний код, ви побачите такі файли класів:

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

Дивіться, що клас - Poly$1.class. Це файл класу, створений компілятором, відповідний анонімному підкласу, який ви створили за допомогою наведеного нижче коду:

new My() {};

Отже, зрозуміло, що існує інший клас, який інстанціюється. Просто це класу присвоюється ім'я лише після складання компілятором.

Загалом усі анонімні підкласи у вашому класі будуть названі таким чином:

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

Ці числа позначають порядок, в якому ці анонімні класи з’являються вкладному класі.


172
@coders. Точна відповідь: - Ви не можете інстанціювати свій абстрактний клас, проте ви можете створити конкретний підклас вашого абстрактного класу.
Rohit Jain

16
В одному рядку ви можете сказати: - Ви ніколи не можете створити абстрактний клас. Ось мета абстрактного класу.
Рахул Трипаті

66
Здається, інтерв'юер більше вкладав у свою відповідь, ніж він був у вас ...
Ніл Т.

7
Згідно з іншим коментаремпосиланням на JLS ), "Об'єкт, як кажуть, є екземпляром свого класу та всіх надкласів свого класу" - отже, чи ми насправді технічно не створюємо тут екземпляр абстрактного класу? тобто примірник абстрактного класу?
Аршаджій

6
@ARS Я б сказав, що існує різниця між тим, щоб бути instance ofта instantiating. Ви створюєте лише один клас, тоді як створений вами об'єкт може бути екземпляром декількох класів через спадкування.
Саймон Форсберг

89

Вищезазначене створює анонімний внутрішній клас, який є підкласом myабстрактного класу. Це не є рівнозначним екземпляру самого абстрактного класу. OTOH, кожен екземпляр підкласу є екземпляром усіх його суперкласів та інтерфейсів, тому більшість абстрактних класів справді створюються інстанцією шляхом інстанціювання одного з їх конкретних підкласів.

Якщо інтерв'юер просто сказав "неправильно!" не пояснюючи, і подав цей приклад, як унікальний контрприклад, я думаю, що він не знає, про що йде мова.


10
Строго кажучи, абстрактний надклас не є примірником. Його конструктор покликаний ініціалізувати змінні екземпляра.
Сприйняття

4
Так, це subclassInstance instanceof SuperClassповернеться true, тому об'єкт є екземпляром надкласу, що означає, що суперклас був інстанційним. Але це просто семантичне нитікання.
JB Nizet

5
Це може бути справді семантика. Java визначає інстанціювання з точки зору створення об'єктів за допомогою нового ключового слова (що з вас неможливо зробити з абстрактним класом). Але, звичайно, конкретний підклас вірно повідомить, що це примірник кожного члена його батьківської ієрархії.
Сприйняття

11
У пункті 4.12.6 JLS сказано: "Об'єкт вважається екземпляром свого класу та всіх надкласів свого класу."
JB Nizet

85

= my() {};означає , що є анонімна реалізація, а не просто створення екземпляр об'єкта, який повинен були бути : = my(). Ніколи не можна інстанціювати абстрактний клас.


30

Лише зауваження, які ви могли зробити:

  1. Чому polyрозширюється my? Це марно ...
  2. Який результат компіляції? Три файлу: my.class, poly.classіpoly$1.class
  3. Якщо ми можемо створити такий абстрактний клас, ми можемо створити інтерфейс теж ... дивно ...


Чи можемо ми створити абстрактний клас?

Ні, ми не можемо. Що ми можемо зробити - створити анонімний клас (це третій файл) та інстанціювати його.


А як з екземпляром суперкласу?

Абстрактний суперклас - це не ми, а ява.

EDIT: Попросіть його перевірити це

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());

}

вихід:

false
class my$1
class my$2

+1 для спостереження 3: наприклад, ми можемо зробити Serializable s = new Serializable() {};(що досить марно) , і якщо позначено на ваш код буде давати class my$3(або будь-який інший огороджувальних клас і номер)
відновимо Моніка - notmaynard

18

Ви можете просто відповісти лише одним рядком

Ні , ви ніколи не можете примірник абстрактного класу

Але інтерв'юер все ще не згоден, тоді ви можете сказати йому / їй

все, що ви можете зробити, це створити анонімний клас.

І, відповідно до класу Anonymous, клас оголошується та інстанціюється в тому самому місці / рядку

Таким чином, можливо, інтерв'юер буде зацікавлений перевірити рівень вашого довіри та наскільки ви знаєте про ООС.


17

Технічна частина була добре висвітлена в інших відповідях, і це, головним чином, закінчується:
"Він помиляється, він не знає матеріалів, попросіть його приєднатися до ТА і все це очистить :)"

Я хотів би звернутися до факту (про який згадувалося в інших відповідях), що це може бути проблемою, яка викликає стрес і є важливим інструментом для багатьох інтерв'юерів, щоб дізнатися більше про вас та як ви реагуєте на складні та незвичні ситуації. Надаючи вам неправильні коди, він, ймовірно, хотів побачити, чи ви сперечалися. Щоб знати, чи маєте ви впевненість протистояти своїм старшим у подібних ситуаціях.

PS: Я не знаю чому, але в мене є відчуття, що інтерв'юер прочитав цю публікацію.


13

Абстрактні класи не можуть бути екземплярами, але вони можуть бути підкласами. Дивіться це посилання

Найкращий приклад - це

Хоча клас календаря має абстрактний метод getInstance () , але коли ви говоритеCalendar calc=Calendar.getInstance();

calc посилається на екземпляр класу GregorianCalendar як "GregorianCalendar розширює календар "

Анонімний внутрішній тип Infact дозволяє створити підклас без імені абстрактного класу та його екземпляр.


11

Технічна відповідь

Абстрактні заняття неможливо створити - це за визначенням та дизайном.

З JLS, Глава 8. Класи:

Названий клас може бути оголошений абстрактним (§8.1.1.1) і повинен бути оголошений абстрактним, якщо він неповно реалізований; такий клас не може бути ініційований, але може бути розширений на підкласи.

Від JSE 6 java doc для Classes.newInstance ():

InstantiationException - якщо цей клас являє собою абстрактний клас, інтерфейс, клас масиву, примітивний тип або void; або якщо в класі немає нульового конструктора; або якщо інстанція виходить з ладу з якоїсь іншої причини.

Звичайно, можна створити конкретний підклас абстрактного класу (включаючи анонімний підклас), а також здійснити набір текстових посилань на об'єкт, що посилається на абстрактний тип.

Різний кут цього - командна гра та соціальний інтелект:

Таке технічне непорозуміння трапляється часто в реальному світі, коли ми маємо справу зі складними технологіями та юридичними характеристиками.

"Навики людей" тут можуть бути важливішими, ніж "Технічні навички". Якщо ви конкурентно і агресивно намагаєтесь довести свою сторону аргументу, то ви можете бути теоретично правильними, але ви також можете зробити більше шкоди в боротьбі / пошкодженні «обличчя» / створення ворога, ніж варто. Будьте примиренні та розуміючі у вирішенні своїх відмінностей. Хто знає - можливо, ви "обидва праві", але відпрацьовуєте трохи інші значення для термінів ??

Хто знає - хоча це не ймовірно, але інтерв'юер навмисно ввів невеликий конфлікт / нерозуміння, щоб поставити вас у складну ситуацію і побачити, як ви поводитесь емоційно та соціально. Будьте доброзичливими та конструктивними з колегами, дотримуйтесь порад старших людей та дотримуйтесь їх після інтерв'ю, щоб вирішити будь-які виклики / непорозуміння - електронною поштою чи телефонним дзвінком. Показує, що ви вмотивовані та орієнтовані на деталі.


7

Це добре встановлений факт , що abstract classможе НЕ бути реалізований , як все відповіли.

Коли програма визначає анонімний клас, компілятор фактично створює новий клас з іншою назвою (має шаблон, EnclosedClassName$nде nномер анонімного класу)

Отже, якщо ви декомпілюєте цей клас Java, ви знайдете код, як показано нижче:

мій клас

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

poly $ 1.class (згенерований клас "анонімного класу")

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

Ні, ви не можете миттєво отримати абстрактний клас. Ми створюємо лише анонімний клас. У абстрактному класі ми оголошуємо абстрактні методи та визначаємо лише конкретні методи.


4

Про абстрактні заняття

  • Неможливо створити об’єкт абстрактного класу
  • Може створювати змінні (може вести себе як типи даних)
  • Якщо дитина не може перемогти хоча б один абстрактний метод батьків, тоді дитина також стає абстрактною
  • Абстрактні заняття марні без дитячих занять

Мета абстрактного класу - поводитись як база. У ієрархії спадкування ви побачите абстрактні класи до вершини.


3

Ви можете сказати:
ми не можемо інстанціювати абстрактний клас, але ми можемо використовувати newключове слово для створення анонімного екземпляра класу, просто додавши {}в якості тіла реалізації в кінці абстрактного класу.


3

Розширення класу не означає, що ви інстанціюєте клас. Насправді у вашому випадку ви створюєте екземпляр підкласу.

Я майже впевнений, що абстрактні заняття не дозволяють ініціювати. Отже, я б сказав: ні: ви не можете створити абстрактний клас. Але, ви можете продовжити / успадкувати.

Ви не можете безпосередньо створити абстрактний клас. Але це не означає, що ви не можете отримати екземпляр класу (не фактично екземпляр оригінального абстрактного класу) опосередковано. Я маю на увазі, що ви не можете створити органічний абстрактний клас, але ви можете:

  1. Створіть порожній клас
  2. Успадкуйте його від абстрактного класу
  3. Миттєвий клас дервів

Таким чином, ви отримуєте доступ до всіх методів та властивостей абстрактного класу через похідний екземпляр класу.


2

Екстрагувати абстрактний клас неможливо. Що ви насправді можете зробити, застосуйте деякі загальні методи в абстрактному класі, а інші дозвольте виконувати їх (оголосивши їх абстрактними) і дозвольте конкретному уникненню їх реалізовувати залежно від їх потреб. Тоді ви можете зробити завод, який повертає екземпляр цього абстрактного класу (власне його реалізатор). Після цього на заводі ви вирішуєте, якого виконавця вибрати. Це відоме як модель дизайну фабрики:

   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
    }

Конкретному реалізатору потрібно лише реалізувати методи, оголошені абстрактними, але матиме доступ до логіки, реалізованої в тих класах абстрактного класу, які не оголошуються абстрактними:

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;
    }
}

Тоді нарешті фабрика виглядає приблизно так:

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;
    }
}

Одержувач AbstractGridManager зателефонував би на його методи та отримав логіку, реалізовану у конкретному спаді (та частково в методах абстрактного класу), не знаючи, яка конкретна реалізація він отримав. Це також відоме як інверсія контролю або введення залежності.


2

Ні, ми не можемо створити об'єкт абстрактного класу, але створимо довідкову змінну абстрактного класу. Довідкова змінна використовується для позначення об'єктів похідних класів (Підкласи класу Анотація)

Ось приклад, який ілюструє це поняття

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()); 
    } 
}

Тут ми бачимо, що ми не можемо створити об’єкт типу Figure, але можемо створити довідкову змінну типу Figure. Тут ми створили довідкову змінну типу Рисунок і Фігура Довідкова змінна класу використовується для позначення об'єктів Класу Прямокутник і Трикутник.


0

Насправді ми не можемо створити об’єкт абстрактного класу безпосередньо. Те, що ми створюємо, є посилальною змінною абстрактного дзвінка Довідкова змінна використовується для посилання на об'єкт класу, який успадковує клас "Абстракт", тобто підклас абстрактного класу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.