Абстрактный класс - это класс, создание которого невозможно. Абстрактный класс используется путем создания наследующего подкласса, для которого может быть создан . Абстрактный класс делает несколько вещей для наследующего подкласса:
- Определить методы, которые могут использоваться наследующим подклассом.
- Определите абстрактные методы, которые должен реализовывать наследующий подкласс.
- Предоставляет общий интерфейс, который позволяет подклассу обмениваться со всеми другими подклассами.
Вот пример:
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()
в 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!"); }
}
Вы не можете реализовать методы в интерфейсе. Это означает, что если вы реализуете два разных интерфейса, разные методы в этих интерфейсах не могут конфликтовать. Поскольку все методы в интерфейсе являются абстрактными, вы должны реализовать метод, и поскольку ваш метод является единственной реализацией в дереве наследования, компилятор знает, что он должен использовать ваш метод.