Вы находитесь на странице: 1из 54

АБСТРАКТНЫЕ КЛАССЫ

И
ИНТЕРФЕЙСЫ

Сергей Довгалец

1
Три кита ООП
Объектно-ориентированное программирование основано на трех принципах:

 Инкапсуляции;
 Наследовании;
 Полиморфизме.
Три кита ООП
Инкапсуляция (encapsulation) - это механизм, который объединяет
данные и код, манипулирующий этими данными, а также защищает и
то, и другое от внешнего вмешательства или неправильного
использования. В объектно-ориентированном программировании код и
данные могут быть объединены вместе; в этом случае говорят, что
создаётся так называемый "чёрный ящик". Когда коды и данные
объединяются таким способом, создаётся объект (object).
Три кита ООП
Наследование (inheritance) - это процесс, посредством которого один объект
может приобретать свойства другого. Точнее, объект может наследовать
основные свойства другого объекта и добавлять к ним черты, характерные только
для него.

Наследование бывает двух видов:


 одиночное - когда каждый класс имеет одного и только одного предка;
 множественное - когда каждый класс может иметь любое количество
предков.
Три кита ООП
Полиморфизм (polymorphism) - это свойство, которое позволяет одно и то же имя
использовать для решения двух или более схожих, но технически разных задач.

Целью полиморфизма, применительно к объектно-ориентированному


программированию, является использование одного имени для задания общих
для класса действий.
В более общем смысле, концепцией полиморфизма является идея "один
интерфейс, множество методов".
Раннее и позднее связывание
Раннее связывание - метод который будет вызван, известен во время
компиляции.

Позднее связывание – вызываемый метод определяется на этапе выполнения.


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

MyClass myClass = new MyClass();


String string = myClass.toString();
Абстрактный класс
Абстрактный класс — базовый класс, abstract class Animal {
который не предполагает создания private String name;
экземпляров. abstract void voice();
}

• Объявляется с ключевым словом abstract и может содержать объявления


абстрактных методов.
• При расширении абстрактного класса все его абстрактные методы
необходимо определить или подкласс также объявить абстрактным. Нельзя
создавать объекты абстрактных классов, однако можно объявлять объектные
переменные.
• Может иметь конструкторы.
• Может иметь поля уровня класса или объекта.
• Методы могут изменять значения полей класса/объекта.
Абстрактный класс
abstract class GraphicObject {
int x;
int y;
public abstract void draw(); //абстрактный метод

public void moveTo(int x, int y) {


this.x = x;
this.y = y;
}
}

class Circle extends GraphicObject {


public void draw(){
//рисуем круг
}
}

class Runner {
public static void main(String[] args) {
GraphicObject object; // можно объявить ссылку
object = new GraphicObject(); //нельзя создать объект!
object = new Circle();
object.draw();
}
}
Интерфейсы
До Java 8:
Java интерфейс — это конструкция, похожая на класс в котором отсутствуют
реализации методов и может содержать константы.
• Если interface объявлен в отдельном файле, то допускаются только
модификаторы public или default.
• Если interface объявлен внутри другого класса (не интерфейса!), то
допускаются все модификаторы видимости public, default, private и protected.
• Если interface объявлен внутри другого интерфейса (не класса!), может иметь
только модификатор public или default.
• Interface объявленный внутри другого класса или интерфейса также может
иметь слово static в модификаторе доступа. Static ничего не меняет, но
ошибкой не является.
• не может иметь своего состояния (все поля public static final)
• метод всегда трактуется компилятором как public abstract.
Реализация интерфейсов
interface Interface1 {
String commonString = "commonString from Interface1";
}

interface Interface2 {
String commonString = "commonString from Interface2";
}

class MixedClass1 implements Interface1,Interface2 {


}

MixedClass1 mixedClass = new MixedClass1();


//System.out.println(mixedClass.commonString); // Error!!!
System.out.println(((Interface2) mixedClass).commonString);

Interface1 Interface = mixedClass;


System.out.println(Interface.commonString);

commonString from Interface2

commonString from Interface1


Реализация интерфейсов
interface Interface3 {
public String testMethod (); MixedClass3 anotherMixedClass = new MixedClass3();
} System.out.println(anotherMixedClass.testMethod());

class Class3 implements Interface3{ System.out.println(new MixedClass4().testMethod());


public String testMethod() {
return "testMethod () from parent class3";
}
}

class MixedClass3 extends Class3 implements Interface3 {


} testMethod () from parent class3
testMethod () from parent class4
class Class4{
public String testMethod() {
return "testMethod () from parent class4";
}
}
class MixedClass4 extends Class4 implements Interface3 {
}
Интерфейсы Java 8
Cтало возможным добавлять реализацию методов в интерфейсах
(default или static -  реализация обязательна.  всегда предполагают
модификатор public):

interface SomeInterface {
default int addOne(); // Extension method should have a body
static int addTwo(); // Static methods in interfaces should have a body

default int addOne(int i) {


return i + 1;
}

static int addTwo(int i) {


return i + 2; SomeInterface.addTwo(22);
} new SomeInterface().addOne(5); // Error!!!
public default int addThree(int i) {
new implSomeIntrfacce().addOne(5);
return i + 3;
}

public static int addFour(int i) {


return i + 4;
}
}
class implSomeIntrfacce implements SomeInterface{}
Интерфейсы Java 8
Если нужно сделать недоступной реализацию метода интерфейса - можно создать
промежуточный интерфейс или абстрактный класс и в нём снова объявить этот
метод как абстрактный.

interface SomeInterface {
default int addOne(int i) {
return i + 1;
}
}

interface SomeInterface1 extends SomeInterface {


int addOne(int i);
}
или
abstract class SomeAClass implements SomeInterface {
public abstract int addOne(int i);
}

Проделать подобное со статическим методом — не получится


Интерфейсы Java 9
Можно создавать private и private static методы и писать их реализацию. Эти
методы используются другими методами интерфейса, позволяя реализовывать
дублирующийся код. Методы private и default могут обращаться к любым другим
методам интерфейса. Методы private static и static только к статическим.

public interface SomeUseful {


default int doWork() {
return process(calculateTime());
}

private int calculateTime() { ...}

private int process() {


int value = calculateSomething(); ...}

private static int calculateSomething() { ...}


}

Концептуально это ничего не меняет, но хороший стиль поддерживать проще.


Интерфейсы
Модификаторы методов интерфейсов:

• public static — корректен. Требует реализации метода.


• public abstract — корректен. Должен быть определён в потомке.
• public default — корректен. Требует реализации метода. Может быть
переопределён в потомке.
• private static — корректен. Требует реализации метода.
• private abstract — невозможен. private не виден потомкам, а значит должен быть
определён тут же, в интерфейсе. И abstract, постулирующий реализацию в
потомке, не может быть назначен. Противоречие.
• private default — невозможен. Ибо default виден всем и может быть
переопределён в потомке. А private — не виден потомкам. Противоречие.
• private — корректен.
Реализация интерфейсов
public interface InterfaceA {
default void defaultMethod() { ...}
}

public interface InterfaceB {


default void defaultMethod() { ...}
}

public class Impl implements InterfaceA, InterfaceB {


public void defaultMethod() {
// Своя реализация ...
}

// Или можно использовать реализацию одного из предков.


public void defaultMethod() {
InterfaceB.super.defaultMethod();
//...
}
}
Интерфейсы.
Вложенные интерфейсы. Интерфейсы можно вложить (объявить
членом) другого класса или интерфейса. В этом случае значение доступа
может принимать значения public, private, protected. Когда вложенный
интерфейс использует вне области вложения, то он используется вместе
с именем класса или интерфейса.

public interface Square {


double PI = 3.1415926;
double square();

public interface InnerSquare{


double getInnerSquare();
}
}
public class Test {
public static void main(String[] args){
Square.InnerSquare innerSquare;
}
}
Интерфейсы
Свойства интерфейсов.
• C помощью оператора new нельзя создать экземпляр интерфейса.
• Можно объявлять интерфейсные ссылки.
• Интерфейсные ссылки должны ссылать на объекты классов, реализующих
данный интерфейс.
• Через интерфейсную ссылку можно вызвать только методы определенные с
интерфейсе.
• С помощью оператора instanseof можно проверять, реализует ли объект
определенный интерфейс.
• Если класс не полностью реализует интерфейс, то он должен быть объявлен
как abstract.
• Интерфейс может быть расширен при помощи наследования от другого
интерфейса, синтаксис в этом случае аналогичен синтаксисом наследования
классов .
Отличие абстрактного класса от интерфейса:

• У абстрактного класса могут быть конструкторы.


• У абстрактного класса помимо констант могут быть поля (уровня класса или
объекта).
• У абстрактного класса методы могут изменять значения полей класса/объекта.
(класс может иметь состояние).
ВНУТРЕННИЕ КЛАССЫ

20
Внутренние классы. Определение
Внутренним классом называют класс, который является членом другого класса.
Существует четыре базовых типа внутренних классов в Java:

• Nested Inner classes (вложенные внутренние классы)

• Static Nested classes or Member of outer class (статические вложенные классы)

• Method Local Inner classes (внутренние классы в локальном методе)

• Anonymous Inner classes (анонимные классы)

Такая возможность используется, если класс более нигде не используется, кроме как в
том, в который он вложен.
Внутренние классы. Inner (нестатические).
• Методы внутреннего класса имеют прямой доступ ко всем полям и методам
внешнего класса.
import java.util.Date;
public class Outer {
private String str;
Date date;

Outer() {
str = "string in outer";
date = new Date();
}
class Inner {
public void method() {
System.out.println(str);
System.out.println(date.getTime());
}
}
}
Внутренние классы. Inner (нестатические)
• Доступ к элементам внутреннего класса возможен из внешнего класса через
объект внутреннего класса. То есть, чтобы класс Outer мог вызвать какой-либо
метод класса Inner в классе Outer необходимо создать объект класса Inner и
вызывать методы уже через этот объект.

import java.util.Date;
public class Outer {
Inner inner;
private String str;
Date date;
Outer() {
str = "string in outer";
date = new Date();
inner = new Inner();
}
class Inner {
public void method() {
System.out.println(str);
System.out.println(date.getDate());
}
}
public void callMethodInInner() {
inner.method();
}
}
Внутренние классы. Inner (нестатические)
• Внутренние классы не могут содержать static-полей, кроме final static
import java.util.Date;
public class Outer {
Inner inner;
private String str;
Date date;
Outer() {
str = "string in outer";
date = new Date();
inner = new Inner();
}
class Inner {
private int i;
public static int static_pole; // ERROR
public final static int pubfsi_pole = 22;
private final static int prfsi_polr = 33;
public void method() {
System.out.println(str);
System.out.println(date.getDate());
}
}
public void callMethodInInner() {
inner.method();
}
}
Внутренние классы. Inner (нестатические).
• Доступ к таким полям можно получить извне класса, используя конструкцию
 
имя_внешнего_класса.имя_внутреннего класса.имя_статической_переменной

public class OuterInnerTest {


public static void main(String[] args) {
Outer outer = new Outer();
System.out.println(Outer.Inner.pubfsi_pole);
}
}
Внутренние классы. Inner (нестатические).
• Также доступ к переменной типа final static возможен во внешнем классе
через имя внутреннего класса:

public class Outer {


Inner inner;
Outer() {
inner = new Inner();
}
class Inner {
public final static int pubfsi_pole = 22;
private final static int prfsi_polr = 33;
}
public void callMethodInInner() {
System.out.println(Inner.prfsi_polr);
System.out.println(Inner.pubfsi_pole);
}
}
Внутренние классы. Inner (нестатические).
• Внутренние классы могут быть производными от других классов. Внутренние
классы могут быть базовыми
public class Outer {
private int privI = 1;
protected int protI = 2;
public int pubI = 3;
class Inner1 {
private int inner1_privI = 11;
protected int inner1_protI = 22;
public int inner1_pubI = 33;
public void print() {
System.out.println(privI + " " + protI + " "
+ pubI + " “ + inner1_privI + " " +
inner1_protI + " " + inner1_pubI);
}
}
Внутренние классы. Inner (нестатические).
class Inner2 extends Inner1 {
private int inner2_privI = 111;
protected int inner2_protI = 222;
public int inner2_pubI = 333;
public void print() {
System.out.println(privI + " " + protI + " " + pubI + " “ +
inner1_protI + " " + inner1_pubI);
System.out.println(inner2_privI + " " + inner2_protI + " “
+ inner2_pubI);
}
}

class Inner3 extends Outer2 {


private int inner3_privI = 1111;
protected int inner3_protI = 2222;
public int inner3_pubI = 3333;
public void print() {
System.out.println(privI + " " + protI + " " + pubI + " “ +
outer2_protI + " " + outer2_pubI);
System.out.println(inner3_privI + " " + inner3_protI + " “
+ inner3_pubI);
}
}
}
Внутренние классы. Inner (нестатические).
• Внутренние классы могут реализовывать интерфейсы
public class Outer {
class Inner1 implements MyInterfaceInner, MyInterfaceOuter {
public void interfaceInnerPrint() {
System.out.println("interfaceInnerPrint");
}
public void interfacePrint() {
System.out.println("interfacePrint");
}
}
interface MyInterfaceInner {
void interfaceInnerPrint();
}
}
interface MyInterfaceOuter {
void interfacePrint();
}
Внутренние классы. Inner (нестатические).
• Внутренние классы могут быть объявлены с параметрами final, abstract, public,
protected, private

public class Outer {


public class Inner1 {
}
private class Inner2 {
}
protected class Inner3 {
}
abstract private class Inner4 {
}
final protected class Inner5 {
}
}
Внутренние классы. Inner (нестатические).
• Если необходимо создать объект внутреннего класса где-нибудь, кроме
внешнего статического метода класса, то нужно определить тип объекта как
имя_внешнего_класса.имя_внутреннего_класса

public class Outer {


public class Inner1{
void print(){
System.out.println("Inner1");
}
}
protected class Inner2{
void print(){
System.out.println("Inner1");
}
}
}

public static void main(String[] args) {


Outer.Inner1 obj1 = new Outer().new Inner1();
Outer.Inner2 obj2 = new Outer().new Inner2();
obj1.print();
obj2.print();
}
Внутренние классы. Inner (нестатические)
• Внутренний класс может быть объявлен внутри метода или логического блока
внешнего класса; видимость класса регулируется видимостью того блока, в
котором он объявлен; однако класс сохраняет доступ ко всем полям и
методам внешнего класса, а также константам, объявленным в текущем
блоке кода.
Внутренние классы. Inner (нестатические).

public class Outer {


public void method() {
final int x = 3;
class Inner1 {
void print() {
System.out.println("Inner1");
System.out.println("x=" + x);
}
}
Inner1 obj = new Inner1();
obj.print();
}
public static void main(String[] args) {
Outer out = new Outer();
out.method();
}
}
Внутренние классы. Inner (нестатические).
• Локальные внутренние классы не объявляются с помощью модификаторов
доступа.

public class Outer {


public void method() {
public class Inner1 {
} // ОШИБКА
}
}
Внутренние классы. Inner (нестатические)
• Правила для внутренних классов.
1) ссылка на внешний класс имеет вид
 
имя_внешнего_класса.this
 
Для получения доступа из внутреннего класса к экземпляру его внешнего класса
необходимо в ссылке указать имя класса и ключевое слово this, поставив между
ними точку (например, OuterClass.this). Ключевое слово this обеспечивает доступ
к потенциально спрятанным методам и полям, в которых внутренние и внешние
классы используют метод или переменную с одинаковыми именами.
Внутренние классы. Inner (нестатические)
Например, в следующем определении класса и у внешнего и у внутреннего
классов присутствует переменная count. Для получения доступа к переменной
внешнего класса, необходимо в ссылке на переменную перед ее именем
приписать префикс this и имя внешнего класса.

class OuterClass {
int count = 0;
class InnerClass {
int count = 10000;
public void display() {
System.out.println("Outer: " + OuterClass.this.count);
System.out.println("Inner: " + count);
}
}
}
Внутренние классы. Inner (нестатические)
• Правила для внутренних классов
2) конструктор внутреннего класса можно создать явным способом
 
ссылка_на_внешний_объект.new
конструктор_внутренего_класса([параметры]);
Вложенные классы. Nested (статические).
• Статический вложенный класс для доступа к нестатическим членам и методам
внешнего класса должен создавать объект внешнего класса

public class Outer {


private int x = 3;
static class Inner1 {
public void method() {
Outer out = new Outer();
System.out.println("out.x=" + out.x);
}
}
}
Вложенные классы. Nested (статические).
• Вложенный класс имеет доступ к статическим полям и методам внешнего
класса

public class Outer {


private int x = 3;
private static int y = 4;
public static void main(String[] args) {
Inner1 in = new Inner1();
in.method();
}
public void meth() {
Inner1 in = new Inner1();
in.method();
}
static class Inner1 {
public void method() {
System.out.println("y=" + y);
// System.out.println("x="+x); // ERROR
Outer out = new Outer();
System.out.println("out.x=" + out.x);
}
}
}
Вложенные классы. Nested (статические).
• Статический метод вложенного класса вызывается при указании полного
относительного пути к нему

public class Outer {


public void meth() {
Inner1.method();
}
static class Inner1 {
public static void method() {
System.out.println("inner static method");
}
}
}

public class Outer2 {


public static void main(String[] args) {
Outer.Inner1.method();
}
}
Вложенные классы. Nested (статические).
• Подкласс вложенного класса не наследует возможность доступа к членам
внешнего класса, которым наделен его суперкласс

public class Outer {


private static int x = 10;
public void meth() {
Inner1.method();
}
static class Inner1 {
public static void method() {
System.out.println("inner1 outer.x=" + x);
}
}
}

public class Outer2 extends Outer.Inner1 {


public static void main(String[] args) {
}
public void outer2Method() {
// System.out.println("x="+x); // ERROR
}
}
Вложенные классы. Nested (статические).
• Класс, вложенный в интерфейс, статический по умолчанию

public interface MyInterface {


int x = 10;
class InnerInInterface {
public void meth() {
System.out.println("x=" + x);
}
}
}
Вложенные классы. Nested (статические).
• Вложенный класс может быть базовым, производным, реализующим
интерфейсы

public class Outer {


private static int x = 10;
public static void main(String[] args) {
Inner2.methodInner1();
Inner2.methodInner2();
Inner3.methodInner1();
Outer out = new Outer();
out.meth();
}
public void meth() {
Inner3 in3 = new Inner3();
in3.methodInner3();
in3.methodInner1();
}
static class Inner1 {
public static void methodInner1() {
System.out.println("inner1 outer.x=" + x);
}
}
Вложенные классы. Nested (статические).

static class Inner2 extends Inner1 {


public static void methodInner2() {
methodInner1();
System.out.println("inner1 outer.x=" + x);
}
}
class Inner3 extends Inner1 implements MyInterface {
public void methodInner3() {
methodInner1();
System.out.println("inner1 outer.y=" + y);
System.out.println("inner1 outer.x=" + x);
}
}
}
interface MyInterface {
int y = 123;
}
Анонимные классы. Anonymous
• Анонимный класс расширяет другой класс или реализует внешний интерфейс
при объявлении одного единственного объекта; остальным будет
соответствовать реализация, определенная в самом классе
Анонимные классы. Anonymous.
public class MyClass {
public void print() {
System.out.println("This is Print() in MyClass");
}
}

class MySecondClass {
public void printSecond() {
MyClass myCl = new MyClass() {
public void print() {
System.out.println("!!!!!!!");
newMeth();
}
public void newMeth() {
System.out.println("New method");
}
};
myCl.print();// myCl.newMeth(); // Error
MyClass myCl2 = new MyClass();
myCl2.print();
}
public static void main(String[] args) {
MySecondClass myS = new MySecondClass();
myS.printSecond();
}
}
Анонимные классы. Anonymous.
• Объявление анонимного класса выполняется одновременно с созданием его
объекта с помощью операции new

public class MySecondClass {


public void printSecond() {
System.out.println("MySecondClass.java::printSecond");
}
public static void main(String[] args) {
MySecondClass myS = new MySecondClass() {
public void printSecond() {
System.out.println("Oi-oi-oi");
}
};
myS.printSecond();
new MySecondClass() {
public void printSecond() {
System.out.println("Ai-ai-ai");
}
}.printSecond();
}
}
Анонимные классы. Anonymous
• Конструкторы анонимных классов ни определить, ни переопределить нельзя

public class MySecondClass {


public MySecondClass() {
System.out.println("Constructor");
}
public void printSecond() {
System.out.println("MySecondClass.java::printSecond");
}
public static void main(String[] args) {
new MySecondClass() {
// public MySecondClass() {} // ERROR
// public MySecondClass(String str){} // ERROR
public void printSecond() {
System.out.println("Ai-ai-ai");
new MyClass() {
public void print() {
System.out.println("print in MyClass in printSecond in MySecondClass");
}
}.print();
}
}.printSecond();
}
}
Анонимные классы. Anonymous.
• Анонимные классы допускают вложенность друг в друга

public class MyClass {


public void print() {
System.out.println("This is Print() in MyClass");
}
}

public class MySecondClass {


public void printSecond() {
System.out.println("MySecondClass.java::printSecond");
}
public static void main(String[] args) {
new MySecondClass() {
public void printSecond() {
System.out.println("Ai-ai-ai");
new MyClass() {
public void print() {
System.out.println("print in MyClass in printSecond in MySecondClass");
}
}.print();
}
}.printSecond();
}
}
Анонимные классы. Anonymous.
• Объявление анонимного класса в перечислении отличается от простого
анонимного класса, поскольку инициализация всех элементов происходит при
первом обращении к типу

enum Color {
Red(1), Green(2), Blue(3) {
int getNumColor() { return 222; }
};
Color(int _num) { num_color = _num;}
int getNumColor() { return num_color;}
private int num_color;
}

Color color;
color = Color.Red;
System.out.println(color.getNumColor());
color = Color.Green;
System.out.println(color.getNumColor());
color = Color.Blue;
1
System.out.println(color.getNumColor()); 2
222
Вложенный класс
Из него видны:
— все (даже private) свойства и методы OuterClass обычные и статические.
— public и protected свойства и методы родителя OuterClass обычные и статические. То есть те, которые
видны в OuterClass.

Его видно:
— согласно модификатору доступа.

Может наследовать:
— обычные классы.
— такие же внутренние классы в OuterClass и его предках.

Может быть наследован:


— таким же внутренним классом в OuterClass.

Может имплементировать интерфейс

Может содержать:
— только обычные свойства и методы (не статические).

Экзэмпляр этого класса создаётся так:

OuterClass outerClass = new OuterClass();


OuterClass.InnerClass innerClass = outerClass.new InnerClass();
Статический вложенный класс
Из него (самого класса) видны:
— статические свойства и методы OuterClass (даже private).
— статические свойства и методы родителя OuterClass public и protected. То есть те, которые видны в OuterClass.

Из его экземпляра видны:


— все (даже private) свойства и методы OuterClass обычные и статические.
— public и protected свойства и методы родителя OuterClass обычные и статические. То есть те, которые видны в
OuterClass.

Его видно:
— согласно модификатору доступа.

Может наследовать:
— обычные классы.
— такие же статические внутренние классы в OuterClass и его предках.

Может быть наследован:


— любым классом:
— вложенным
— не вложенным
— статическим
— не статическим!

Может имплементировать интерфейс

Может содержать:
— статические свойства и методы.
— не статические свойства и методы.

Экзэмпляр этого класса создаётся так:


OuterClass.StaticInnerClass staticInnerClass = new OuterClass.StaticInnerClass();
Локальный класс
Из него видны:
— все (даже private) свойства и методы OuterClass обычные и статические.
— public и protected свойства и методы родителя OuterClass обычные и
статические. То есть те, которые видны в OuterClass.

Его видно:
— только в том методе где он определён.

Может наследовать:
— обычные классы.
— внутренние классы в OuterClass и его предках.
— такие же локальные классы определённые в том же методе.

Может быть наследован:


— таким же локальным классом определённом в том же методе.

Может имплементировать интерфейс

Может содержать:
— только обычные свойства и методы (не статические).
Анонимный класс
Из него видны:
— все (даже private) свойства и методы OuterClassа обычные и статические.
— public и protected свойства и методы родителя OuterClassа обычные и
статические. То есть те, которые видны в OuterClassе.

Его видно:
— только в том методе где он определён.

Не может быть наследован

Может содержать:
— только обычные свойства и методы (не статические).