Можем ли мы создать экземпляр абстрактного класса?


574

Во время одного из моих интервью меня спросили: «Можем ли мы создать экземпляр абстрактного класса?»

Мой ответ был «Нет, мы не можем». Но интервьюер сказал мне: «Неправильно, мы можем».

Я немного поспорил об этом. Затем он сказал мне попробовать это дома.

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

Ответы:


723

Здесь я создаю экземпляр моего класса

Нет, вы не создаете экземпляр своего абстрактного класса здесь. Скорее вы создаете экземпляр анонимного подкласса вашего абстрактного класса. И затем вы вызываете метод из вашей ссылки на абстрактный класс, указывающий на объект подкласса .

Это поведение четко указано в JLS - Раздел № 15.9.1 : -

Если выражение создания экземпляра класса заканчивается в теле класса, то создаваемый экземпляр класса является анонимным классом. Затем:

  • Если T обозначает класс, то объявляется анонимный прямой подкласс класса с именем T. Это ошибка времени компиляции, если класс, обозначенный как T, является конечным классом.
  • Если T обозначает интерфейс, то объявляется анонимный прямой подкласс Object, который реализует интерфейс с именем T.
  • В любом случае, тело подкласса - это ClassBody, указанный в выражении создания экземпляра класса.
  • Класс, который создается, является анонимным подклассом.

Акцент мой.

Также в JLS - Раздел № 12.5 вы можете прочитать о процессе создания объекта . Я приведу одно утверждение из этого здесь: -

Всякий раз, когда создается новый экземпляр класса, для него выделяется пространство памяти с местом для всех переменных экземпляра, объявленных в типе класса, и всех переменных экземпляра, объявленных в каждом суперклассе типа класса, включая все переменные экземпляра, которые могут быть скрыты.

Непосредственно перед возвращением ссылки на вновь созданный объект в качестве результата указанный конструктор обрабатывается для инициализации нового объекта с помощью следующей процедуры:

Вы можете прочитать о полной процедуре по ссылке, которую я предоставил.


Чтобы практически увидеть, что создаваемый класс является анонимным подклассом , вам просто нужно скомпилировать оба ваших класса. Предположим, вы поместили эти классы в два разных файла:

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

Эти числа обозначают порядок, в котором эти анонимные классы появляются во включающем классе.


173
@coders. Точный ответ: - Вы не можете создать экземпляр своего абстрактного класса, однако можете создать конкретный подкласс своего абстрактного класса.
Рохит Джайн

17
В одной строке вы можете сказать: - Вы никогда не можете создать экземпляр абстрактного класса. Это цель абстрактного класса.
Рахул Трипати

66
Похоже, что интервьюер был больше вложен в свой ответ, чем в ваш ...
Нил Т.

7
Согласно другому комментарию (со ссылкой на JLS ), «объект называется экземпляром своего класса и всех суперклассов своего класса» - следовательно, разве мы не создаем технически экземпляр абстрактного класса здесь? то есть создание экземпляра абстрактного класса?
Аршаджи

6
@ АРС Я бы сказал, что есть разница между тем, чтобы быть instance ofи instantiating. Вы создаете экземпляр только одного класса, а созданный вами объект может быть экземпляром нескольких классов из-за наследования.
Саймон Форсберг

90

Вышеприведенный пример создает анонимный внутренний класс, который является подклассом myабстрактного класса. Это не совсем эквивалентно созданию самого абстрактного класса. OTOH, каждый экземпляр подкласса является экземпляром всех его суперклассов и интерфейсов, поэтому большинство абстрактных классов действительно создаются путем создания экземпляров одного из их конкретных подклассов.

Если интервьюер просто сказал "неправильно!" не объясняя и привел этот пример как уникальный контрпример, я думаю, что он, тем не менее, не знает, о чем говорит.


10
Строго говоря, абстрактный суперкласс не создается. Его конструктор вызывается для инициализации переменных экземпляра.
Восприятие

4
Да, это так: subclassInstance instanceof SuperClassвозвращает true, поэтому объект является экземпляром суперкласса, что означает, что суперкласс был создан. Но это просто семантические придирки.
JB Низет

5
Может быть, семантика действительно. Java определяет создание экземпляров в терминах создания объектов с помощью ключевого слова new (чего нельзя сделать с абстрактным классом). Но, конечно, конкретный подкласс сообщит правильно, что это экземпляр каждого члена его родительской иерархии.
Восприятие

11
параграф 4.12.6 JLS гласит: «Объект называется экземпляром своего класса и всех суперклассов своего класса».
JB Nizet

85

= my() {};означает , что есть анонимная реализация, а не просто создание экземпляр объекта, который должен были быть : = my(). Вы никогда не можете создать экземпляр абстрактного класса.


30

Просто наблюдения, которые вы могли бы сделать:

  1. Почему polyрасширяется my? Это бесполезно ...
  2. Каков результат компиляции? Три файла: my.class, poly.classиpoly$1.class
  3. Если мы можем создать экземпляр абстрактного класса таким образом, мы можем создать экземпляр интерфейса тоже ... странно ...


Можем ли мы создать экземпляр абстрактного класса?

Нет, мы не можем. Мы можем создать анонимный класс (это третий файл) и создать его экземпляр.


А как насчет создания суперкласса?

Абстрактный суперкласс создается не нами, а Java.

РЕДАКТИРОВАТЬ: Попросите его проверить это

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

Вы можете просто ответить, в одну строку

Нет , вы никогда не сможете создать экземпляр Abstract Class

Но, интервьюер все еще не согласен, тогда вы можете сказать ему / ей

все, что вы можете сделать, это создать анонимный класс.

И, согласно классу Anonymous, класс объявлен и создан в одном месте / строке

Так что, возможно, интервьюеру было бы интересно проверить уровень вашей достоверности и то, сколько вы знаете об ООП.


17

Техническая часть была хорошо освещена в других ответах, и в основном она заканчивается:
«Он не прав, он ничего не знает, попросите его присоединиться к SO и очистить все :)»

Я хотел бы остановиться на том факте (о котором упоминалось в других ответах), что это может быть стресс-вопрос и важный инструмент для многих интервьюеров, чтобы узнать о вас больше и как вы реагируете на сложные и необычные ситуации. Давая вам неправильные коды, он, вероятно, хотел увидеть, если вы спорили в ответ. Чтобы узнать, есть ли у вас уверенность, чтобы противостоять своим старшим в ситуациях, подобных этой.

PS: я не знаю почему, но у меня есть ощущение, что интервьюер прочитал этот пост.


13

Абстрактные классы не могут быть созданы, но они могут быть разделены на подклассы. Смотрите эту ссылку

Лучший пример

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

calc ссылается на экземпляр класса GregorianCalendar как «GregorianCalendar расширяет календарь »

Infact annonymous внутренний тип позволяет создать не имя подкласс абстрактного класса и экземпляр этого.


11

Технический ответ

Абстрактные классы не могут быть созданы - это по определению и замыслу.

Из JLS, Глава 8. Классы:

Именованный класс может быть объявлен абстрактным (§8.1.1.1) и должен быть объявлен абстрактным, если он реализован не полностью; такой класс не может быть создан, но может быть расширен подклассами.

Из JSE 6 Java-документ для 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. Здесь мы создали ссылочную переменную типа Figure, а ссылочная переменная класса Class используется для ссылки на объекты класса Rectangle и Triangle.


0

На самом деле мы не можем создать объект абстрактного класса напрямую. Мы создаем ссылочную переменную абстрактного вызова. Ссылочная переменная используется для ссылки на объект класса, который наследует абстрактный класс, то есть подкласс абстрактного класса.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.