Абстрактный класс на Java


274

Что такое «абстрактный класс» в Java?


35
+1 Этот вопрос настолько простой и фундаментальный, что это классика для SO. Я удивлен, что об этом раньше не спрашивали.
Юваль

6
-1 за комментарий Клемента (если бы я мог); lmgtfy не является полезным ответом. Почему, прочитайте, например, этот meta.stackexchange.com/questions/5280/embrace-the-non-googlers
Джоник

26
@tuergeist. Это не имеет значения, если это легко для Google, если об этом раньше не спрашивали на SO. Кроме того, кто сказал, что вопросы начинающих о языках программирования не относятся к SO?
Джоник

12
Одна вещь, которая мне нравится в SO, - это то, что вы получите сжатый, хорошо сформулированный и точный ответ без какой-либо обычной BS, найденной в остальной части сети ... Ну, в любом случае, что-то подобное. +1 за вопрос!
Андерс Ханссон

1
ТАК не должен иметь длинный хвост! J & J даже говорят об этом вокруг подкаста 56 ...
kwutchak

Ответы:


342

Абстрактный класс - это класс, который не может быть создан. Абстрактный класс используется путем создания наследующего подкласса, который может быть создан. Абстрактный класс делает несколько вещей для наследующего подкласса:

  1. Определите методы, которые могут использоваться наследующим подклассом.
  2. Определите абстрактные методы, которые должен реализовывать наследующий подкласс.
  3. Обеспечить общий интерфейс, который позволяет подклассу обмениваться со всеми другими подклассами.

Вот пример:

abstract public class AbstractClass
{
    abstract public void abstractMethod();
    public void implementedMethod() { System.out.print("implementedMethod()"); }
    final public void finalMethod() { System.out.print("finalMethod()"); }
}

Обратите внимание, что у abstractMethod () нет тела метода. Из-за этого вы не можете сделать следующее:

public class ImplementingClass extends AbstractClass
{
    // ERROR!
}

Там нет метода, который реализует abstractMethod()! Так что у JVM нет возможности узнать, что она должна делать, когда получит что-то вродеnew ImplementingClass().abstractMethod() .

Вот правильный ImplementingClass.

public class ImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("abstractMethod()"); }
}

Обратите внимание, что вам не нужно определять implementedMethod()или finalMethod(). Они уже были определеныAbstractClass .

Вот еще один правильный ImplementingClass.

public class ImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("abstractMethod()"); }
    public void implementedMethod() { System.out.print("Overridden!"); }
}

В этом случае вы переопределили implementedMethod() .

Однако из-за finalключевого слова следующее невозможно.

public class ImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("abstractMethod()"); }
    public void implementedMethod() { System.out.print("Overridden!"); }
    public void finalMethod() { System.out.print("ERROR!"); }
}

Вы не можете сделать это, потому что реализация finalMethod()in AbstractClassпомечена как окончательная реализацияfinalMethod() : никакие другие реализации не будут разрешены, никогда.

Теперь вы также можете дважды реализовать абстрактный класс:

public class ImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("abstractMethod()"); }
    public void implementedMethod() { System.out.print("Overridden!"); }
}

// In a separate file.
public class SecondImplementingClass extends AbstractClass
{
    public void abstractMethod() { System.out.print("second abstractMethod()"); }
}

Теперь где-то вы могли бы написать другой метод.

public tryItOut()
{
    ImplementingClass a = new ImplementingClass();
    AbstractClass b = new ImplementingClass();

    a.abstractMethod();    // prints "abstractMethod()"
    a.implementedMethod(); // prints "Overridden!"     <-- same
    a.finalMethod();       // prints "finalMethod()"

    b.abstractMethod();    // prints "abstractMethod()"
    b.implementedMethod(); // prints "Overridden!"     <-- same
    b.finalMethod();       // prints "finalMethod()"

    SecondImplementingClass c = new SecondImplementingClass();
    AbstractClass d = new SecondImplementingClass();

    c.abstractMethod();    // prints "second abstractMethod()"
    c.implementedMethod(); // prints "implementedMethod()"
    c.finalMethod();       // prints "finalMethod()"

    d.abstractMethod();    // prints "second abstractMethod()"
    d.implementedMethod(); // prints "implementedMethod()"
    d.finalMethod();       // prints "finalMethod()"
}

Обратите внимание , что даже если мы объявили bв AbstractClassтип, он отображает "Overriden!". Это потому, что объект, который мы создали ImplementingClass, былimplementedMethod() конечно, переопределен. (Возможно, вы видели, что это называется полиморфизмом.)

Если мы хотим получить доступ к члену, специфичному для определенного подкласса, мы должны сначала обратиться к этому подклассу:

// Say ImplementingClass also contains uniqueMethod()
// To access it, we use a cast to tell the runtime which type the object is
AbstractClass b = new ImplementingClass();
((ImplementingClass)b).uniqueMethod();

Наконец, вы не можете сделать следующее:

public class ImplementingClass extends AbstractClass, SomeOtherAbstractClass
{
    ... // implementation
}

Только один класс может быть продлен за один раз. Если вам нужно расширить несколько классов, они должны быть интерфейсами. Ты можешь сделать это:

public class ImplementingClass extends AbstractClass implements InterfaceA, InterfaceB
{
    ... // implementation
}

Вот пример интерфейса:

interface InterfaceA
{
    void interfaceMethod();
}

Это в основном так же, как:

abstract public class InterfaceA
{
    abstract public void interfaceMethod();
}

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

Следующее является незаконным:

interface InterfaceB
{
    void interfaceMethod() { System.out.print("ERROR!"); }
}

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


5
@Imagist -1 за неправильное описание для оператора c.implementedMethod (); // выводит "connectedMethod ()", выводит "Overriden!" всегда
Сачин Кумар

2
@ Sachin Я потратил полчаса, пытаясь понять, почему будет напечатано «ImplementMethod ()», а затем я увидел ваш комментарий. Что-то изменилось с Java или другие просто упустили из виду ошибку?
Рунак

@SachinKumar Из-за отсутствия ответа автора я взял на себя обязательство исправить эту ошибку. CMIIW.
Матин Улхак

@SachinKumar Я немного опоздал с игрой здесь, но вы бы сказали, что хорошей аналогией будет объявление метода (но не реализация) в заголовочном файле C ++?
Швайц

5
@SachinKumar зачем c.implementedMethod()печатать "Переопределено!"? SecondImplementingClassне отменяет implementedMethod().
Джон Ред

75

Класс Java становится абстрактным при следующих условиях:

1. По крайней мере один из методов помечен как абстрактный:

public abstract void myMethod()

В этом случае компилятор заставляет вас пометить весь класс как абстрактный.

2. Класс помечается как абстрактный:

abstract class MyClass

Как уже было сказано: если у вас есть абстрактный метод, компилятор заставляет вас пометить весь класс как абстрактный. Но даже если у вас нет абстрактного метода, вы все равно можете пометить класс как абстрактный.

Общего пользования:

Обычное использование абстрактных классов - предоставить схему класса, похожего на интерфейс. Но, в отличие от интерфейса, он уже может обеспечивать функциональность, то есть некоторые части класса реализованы, а некоторые просто обозначены объявлением метода. ("Аннотация")

Абстрактный класс не может быть создан, но вы можете создать конкретный класс на основе абстрактного класса, который затем может быть создан. Для этого вы должны наследовать от абстрактного класса и переопределять абстрактные методы, то есть реализовывать их.


1
Nitpick: второе «условие» является избыточным, поскольку вы можете объявить абстрактный метод только в классе, который явно объявлен как абстрактный.
Стивен К

2
Согласен, совет не совсем правильный, или хорошо написан, он просто отформатирован.
Шелковый полдень

Но ваш совет «сделать класс конкретным» также сформулирован неверно. Вы не делаете класс конкретным, он либо есть, либо нет, в зависимости от того, является ли он абстрактным или нет.
Шелковый полдень

1
Это просто неправильно. Абстрактный класс не должен иметь никаких абстрактных методов. Вы можете создать абстрактный класс без методов или только с конкретными методами.
Йорн

1
10 лет опаздывают на игру, но это самый точный ответ. @ Джорн, ты, наверное, запутался с ответом. Я уверен, что подразумевается, что abstractключевое слово - это все, что необходимо для того, чтобы класс был абстрактным. Но конкретный класс не может содержать abstract метод . Таким образом, если у вашего класса есть abstractметод, он должен быть объявлен как abstractкласс компилятору.
Ракиб

24

Класс, который объявлен с использованием абстрактного ключевого слова, известен как abstract class. Абстракция - это процесс сокрытия деталей реализации данных и отображения только функциональности для пользователя. Абстракция позволяет вам сосредоточиться на том, что делает объект, а не на том, как он это делает.

Основные вещи абстрактного класса

  • Абстрактный класс может содержать или не содержать абстрактные методы. Это могут быть не абстрактные методы.

    Абстрактный метод - это метод, который объявлен без реализации (без фигурных скобок и сопровождается точкой с запятой), например:

    пример: abstract void moveTo(double deltaX, double deltaY);

  • Если у класса есть хотя бы один абстрактный метод, тогда этот класс должен быть абстрактным

  • Абстрактные классы не могут быть созданы (вы не можете создавать объекты класса Abstract)

  • Чтобы использовать абстрактный класс, вы должны наследовать его от другого класса. Предоставить реализации для всех абстрактных методов в нем.

  • Если вы наследуете абстрактный класс, вы должны предоставить реализации для всех абстрактных методов в нем.

Объявите абстрактный класс Указывая abstractключевое слово перед классом во время объявления, он становится абстрактным. Посмотрите на код ниже:

abstract class AbstractDemo{ }

Объявите абстрактный метод Указание abstractключевого слова перед методом во время объявления делает его абстрактным. Посмотрите на код ниже,

abstract void moveTo();//no body

Почему нам нужно абстрагировать классы

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

Это идеальная ситуация для абстрактного суперкласса. Вы можете воспользоваться сходствами и объявить все графические объекты наследуемыми от того же абстрактного родительского объекта (например, :), GraphicObjectкак показано на следующем рисунке. введите описание изображения здесь

Сначала вы объявляете абстрактный класс GraphicObjectдля предоставления переменных-членов и методов, которые полностью используются всеми подклассами, такими как текущая позиция и метод moveTo. GraphicObjectтакже объявлены абстрактные методы, такие как рисование или изменение размера, которые должны быть реализованы всеми подклассами, но должны быть реализованы различными способами. GraphicObjectКласс может выглядеть следующим образом :

abstract class GraphicObject {

  void moveTo(int x, int y) {
    // Inside this method we have to change the position of the graphic 
    // object according to x,y     
    // This is the same in every GraphicObject. Then we can implement here. 
  }

  abstract void draw(); // But every GraphicObject drawing case is 
                        // unique, not common. Then we have to create that 
                        // case inside each class. Then create these    
                        // methods as abstract 
  abstract void resize();
}

Использование абстрактного метода в суб классов каждого не абстрактные подклассы GraphicObject, такие как Circleи Rectangle, должны обеспечивать реализацию для drawи resizeметодов.

class Circle extends GraphicObject {
  void draw() {
    //Add to some implementation here
  }
  void resize() {
    //Add to some implementation here   
  }
}
class Rectangle extends GraphicObject {
  void draw() {
    //Add to some implementation here
  }
  void resize() {
    //Add to some implementation here
  }
}

Внутри mainметода вы можете вызвать все методы следующим образом:

public static void main(String args[]){
   GraphicObject c = new Circle();
   c.draw();
   c.resize();
   c.moveTo(4,5);   
}

Способы достижения абстракции в Java

Есть два способа добиться абстракции в Java

  • Абстрактный класс (от 0 до 100%)
  • Интерфейс (100%)

Абстрактный класс с конструкторами, членами данных, методами и т. Д.

abstract class GraphicObject {

  GraphicObject (){
    System.out.println("GraphicObject  is created");
  }
  void moveTo(int y, int x) {
       System.out.println("Change position according to "+ x+ " and " + y);
  }
  abstract void draw();
}

class Circle extends GraphicObject {
  void draw() {
    System.out.println("Draw the Circle");
  }
}

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

   GraphicObject  grObj = new Circle ();
   grObj.draw();
   grObj.moveTo(4,6);
 }
}

Вывод:

GraphicObject  is created
Draw the Circle
Change position according to 6 and 4

Помните два правила:

  • Если в классе мало абстрактных методов и мало конкретных методов, объявите его как abstractкласс.

  • Если класс имеет только абстрактные методы, объявите его как interface.

Ссылки:


Почему порядок параметров x и y в moveTo отличается в приведенном выше примере, приведенном ниже примере и выводе из приведенного ниже примера? Если мы пытаемся проиллюстрировать важность таких понятий, как интерфейсы и абстрактные классы, не должны ли мы использовать те же сигнатуры функций, что и интерфейсы или абстрактные классы, которые мы последовательно реализуем или расширяем?
Джонатан Рис

два правила отдают это
LiNKeR

4

Это класс, который не может быть создан, и вынуждает реализацию классов, возможно, реализовывать абстрактные методы, которые он выделяет.


3

Проще говоря, вы можете думать об абстрактном классе как об интерфейсе с немного большими возможностями.

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

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

public abstract class MyAbstractClass{
  public abstract void DoSomething();
}

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

Пример:

public abstract class MyAbstractClass{

  public int CalculateCost(int amount){
     //do some default calculations
     //this can be overriden by subclasses if needed
  }

  //this MUST be implemented by subclasses
  public abstract void DoSomething();
}

Этот ответ не поможет, если ОП не знает, что такое интерфейс. Поскольку абстрактные классы и интерфейсы взаимосвязаны, очень маловероятно, что OP будет знать один, не зная другого.
Imagist

Но это может быть. Может быть, он просто знает, что такое интерфейс и как он работает, а затем он сталкивается с абстрактными классами и задается вопросом, зачем они нужны. Не может ли это быть?
Юри

3

Из оракульной документации

Абстрактные методы и классы:

Абстрактный класс - это класс, который объявлен как абстрактный - он может включать или не включать абстрактные методы

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

Абстрактный метод - это метод, который объявлен без реализации (без фигурных скобок и сопровождается точкой с запятой), например так:

abstract void moveTo(double deltaX, double deltaY);

Если класс включает абстрактные методы, то сам класс должен быть объявлен абстрактным, как в:

public abstract class GraphicObject {
   // declare fields
   // declare nonabstract methods
   abstract void draw();
}

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

Поскольку abstract classesи interfacesсвязаны, взгляните на ниже вопросы SE:

В чем разница между интерфейсом и абстрактным классом?

Как мне объяснить разницу между интерфейсом и абстрактным классом?


3

Получите ваши ответы здесь:

Абстрактный класс против интерфейса в Java

Может ли абстрактный класс иметь конечный метод?

Кстати, это вопрос, который вы задали недавно. Подумайте о новом вопросе для создания репутации ...

Редактировать:

Только что понял, что постеры этого и упомянутых вопросов имеют одинаковое или хотя бы похожее имя, но идентификатор пользователя всегда различен. Так что либо есть техническая проблема, у кейера возникают проблемы с повторным входом в систему и поиском ответов на его вопросы, либо это своего рода игра для развлечения сообщества SO;)


И именно поэтому я проверил «сообщество вики» - нельзя повышать репутацию, реагируя на эти вопросы;)
Andreas Dolk

1

Маленькое дополнение ко всем этим постам.

Иногда вы можете объявить класс, но не знаете, как определить все методы, принадлежащие этому классу. Например, вы можете объявить класс с именем Writer и включить в него метод-член с именем write () . Однако вы не знаете, как написать () код, потому что он отличается для каждого типа устройств записи. Конечно, вы планируете справиться с этим путем создания подкласса Writer, такого как принтер, диск, сеть и консоль.


1

Абстрактный класс не может быть непосредственно создан, но должен быть получен из, чтобы его можно было использовать. Класс ДОЛЖЕН быть абстрактным, если он содержит абстрактные методы: либо напрямую

abstract class Foo {
    abstract void someMethod();
}

или косвенно

interface IFoo {
    void someMethod();
}

abstract class Foo2 implements IFoo {
}

Однако класс может быть абстрактным, не содержащим абстрактных методов. Это способ предотвратить прямую инстанцию, например,

abstract class Foo3 {
}

class Bar extends Foo3 {

}

Foo3 myVar = new Foo3(); // illegal! class is abstract
Foo3 myVar = new Bar(); // allowed!

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

Другой частый шаблон заключается в реализации основных функций в абстрактном классе и определении части алгоритма в абстрактном методе, который будет реализован расширяемым классом. Глупый пример:

abstract class Processor {
    protected abstract int[] filterInput(int[] unfiltered);

    public int process(int[] values) {
        int[] filtered = filterInput(values);
        // do something with filtered input
    }
}

class EvenValues extends Processor {
    protected int[] filterInput(int[] unfiltered) {
        // remove odd numbers
    }
}

class OddValues extends Processor {
    protected int[] filterInput(int[] unfiltered) {
        // remove even numbers
    }
}

1

Решение - базовый класс (аннотация)

public abstract class Place {

String Name;
String Postcode;
String County;
String Area;

Place () {

        }

public static Place make(String Incoming) {
        if (Incoming.length() < 61) return (null);

        String Name = (Incoming.substring(4,26)).trim();
        String County = (Incoming.substring(27,48)).trim();
        String Postcode = (Incoming.substring(48,61)).trim();
        String Area = (Incoming.substring(61)).trim();

        Place created;
        if (Name.equalsIgnoreCase(Area)) {
                created = new Area(Area,County,Postcode);
        } else {
                created = new District(Name,County,Postcode,Area);
        }
        return (created);
        }

public String getName() {
        return (Name);
        }

public String getPostcode() {
        return (Postcode);
        }

public String getCounty() {
        return (County);
        }

public abstract String getArea();

}

1
попробуйте отформатировать весь код как код, и, пожалуйста, добавьте некоторые пояснения, сейчас это вряд ли можно считать ответом.
Ном

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

0

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

Другими словами, класс, который объявлен с абстрактным ключевым словом, известен как абстрактный класс в Java. Он может иметь абстрактный (метод без тела) и неабстрактный методы (метод с телом).

Важное примечание: - Абстрактные классы нельзя использовать для создания экземпляров объектов, их можно использовать для создания ссылок на объекты, поскольку подход Java к полиморфизму во время выполнения реализован с использованием ссылок на суперклассы. Таким образом, должна быть возможность создать ссылку на абстрактный класс, чтобы его можно было использовать для указания на объект подкласса. Вы увидите эту функцию в следующем примере

abstract class Bike{  
  abstract void run();  
}  

class Honda4 extends Bike{  
    void run(){
        System.out.println("running safely..");
    }  

    public static void main(String args[]){  
       Bike obj = new Honda4();  
       obj.run();  
    }  
} 

0

Абстрактный класс - это класс, который не полностью реализован, но предоставляет что-то вроде схемы для подклассов. Он может быть частично реализован тем, что содержит полностью определенные конкретные методы, но он также может содержать абстрактные методы. Это методы с сигнатурой, но без тела метода. Любой подкласс должен определять тело для каждого абстрактного метода, в противном случае он также должен быть объявлен абстрактным. Поскольку абстрактные классы не могут быть созданы, они должны быть расширены как минимум на один подкласс, чтобы их можно было использовать. Думайте об абстрактном классе как об общем классе, и подклассы должны заполнить недостающую информацию.


0

Класс, который может иметь как конкретные, так и не конкретные методы, т.е. с телом и без.

  1. Методы без реализации должны содержать ключевое слово abstract.
  2. Абстрактный класс не может быть создан.

-1

Он ничего не делает, просто предоставляет общий шаблон, который будет использоваться для его подкласса

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