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

Презентация:



Advertisements
Похожие презентации
1 © Luxoft Training 2012 Java: расширенные вопросы Модуль #8.
Advertisements

1 Java 6. ИНТЕРФЕЙСЫ И ВНУТРЕННИЕ КЛАССЫ. 2 Интерфейсы Не являются классами Ни один из объявленных методов не может быть реализован внутри интерфейса.
Основы информатики Классы Заикин Олег Сергеевич zaikin.all24.org
Лекция 4. Введение в С++ Наследование, множественное наследование. Конструкторы, деструкторы. Виртуальные функции.
1 Java 6. ИНТЕРФЕЙСЫ И ВНУТРЕННИЕ КЛАССЫ. 2 Интерфейсы Не являются классами Ни один из объявленных методов не может быть реализован внутри интерфейса.
1 Классы в Java Ключевое слово class означает: Я говорю тебе, как выглядит новый тип объекта. Класс является базовым элементом объектно-ориентированного.
Объектно-ориентированный подход в языке C#. Класс в языке C# - ссылочный тип, определенный пользователем. Для классов ЯП C# допустимо только единичное.
Лекция 8 Область видимости Время жизни. Область видимости Область видимости – характеристика именованного объекта Область видимости - часть текста программы,
Наследование Полиморфизм ВЫЗОВ КОНСТРУКТОРОВ И ДЕСТРУКТОРОВ ПРИ НАСЛЕДОВАНИИ.
Встроенные классы в Java Макаревич Л. Г.. Разновидности встроенных классов Вложенные (Nested) классы Внутренние (Inner) классы Локальные классы Анонимные.
Дружественные функции Дружественные функции – это функции, объявленные вне класса, но имеющие доступ к закрытым и защищенным полям данного класса Дружественная.
Полиморфизм. Полиморфизм – это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта.
Лекция 10 ОбъектыЛекция 10 ОбъектыООП Инкапсуляция Возможность совместного хранения данных и кода для их обработки Наследование Возможность расширять существующие.
Наследование Наследование – это отношение является между классами. class Person { string first_name; int birth_year;... } class Student : Person { float.
Практическое занятие 6. Функции. Большинство языков программирования используют понятия функции и процедуры. C++ формально не поддерживает понятие процедуры,
Преобразования типов В языке C/C++ имеется несколько операций преобразования типов. Они используются в случае, если переменная одного типа должна рассматриваться.
Кафедра ОСУ, Java 2007 Слайд 1 Определение классов и методов Java- программа состоит из объектов различных классов, взаимодействующих друг.
Java, каф.ОСУ, АВТФ1 Определение классов и методов Java- программа состоит из объектов различных классов, взаимодействующих друг с другом. Каждое.
Обработка исключительных ситуаций Исключительная ситуация (исключение) – это ошибка, возникающая во время выполнения программы. Например, ошибка работы.
Основы ООП и C# Работа с объектами и классами. Классы Класс специальный тип данных для описания объектов. Он определяет данные и поведение типа. Определение.
Транксрипт:

Особенности наследования интерфейсов (3) Однако, если два интерфейса содержат методы с одинаковой сигнатурой, но разными контрактами (не совпадают типы возвращаемых значений), то их нельзя указывать вместе в качестве родителей одного класса: interface A { int getValue(); … } interface B { double getValue(); … } class NoName implements A, B {// здесь ошибка компиляции … } Это единственное ограничение на множественное наследование от интерфейсов.

Интерфейсы-маркеры Есть случаи, когда полезно определять совершенно пустой интерфейс. Таковы, например, интерфейсы java.lang.Cloneable или java.io.Serializable. Классы, наследующие от них, не должны реализовывать никаких методов интерфейса. Единственное назначение наследования от таких интерфейсов – проверка принадлежности экземпляра к нему с помощью операции instanceof, например так: if ( object instanceof Cloneadble) copyOfObject = object.clone( ); else copyOfObject = null; Такие интерфейсы называются интерфейсами-маркерами, с помощью которых во время исполнения программы можно получить дополнительную информацию об объектах.

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

Применение интерфейсов и абстрактных классов (2) Поэтому в одних ситуациях правильным выбором является интерфейс, в других – абстрактный класс, в третьих – их разумное сочетание. Например, для расширения ранее рассматривавшегося примера обработки геометрических фигур, заключающегося в добавлении координат охватывающего прямоугольника, решение может заключаться в: определении типа как полностью абстрактного интерфейса; создании абстрактного класса, который наследует этот интерфейс и реализует некоторую общую функциональность; и, наконец, определения реальных (не абстрактных) классов, каждый из которых является потомком абстрактного класса и реализует все необходимые методы. Существуют типичные способы сочетания (композиции) интерфейсов, абстрактных и реальных классов, называемые шаблонами проектирования (design patterns).

Применение интерфейсов и абстрактных классов. Пример(1) public interface Placeable { public void setPosition( double left, double top ); public void setSize( double width, double height ); public void moveBy( double deltaX, double deltaY ); public double getSpace( ); public double getPerimeter( ); public boolean isInside( double x, double y ); public double getLeft( ); public double getTop( ); public double getWidth( ); public double getHeight( ); }

Применение интерфейсов и абстрактных классов. Пример(2) public abstract class AbstractFigure implements Placeable { protected double left ; protected double top ; protected double width ; protected double height ; public void setPosition( double left, double top ) { this.left = left ; this.top = top ; } public void setSize( double width, double height ) { this.width = width ; this.height = height ; } public void moveBy( double deltaX, double deltaY ) { this.left += deltaX ; this.top += deltaY ; } public double getLeft( ) { return left; } public double getTop( ) { return top; } public double getWidth( ) { return width; } public double getHeight( ) { return height; } }

Применение интерфейсов и абстрактных классов. Пример(3) class Circle extends AbstractFigure { protected double radius; public Circle( double r ){ radius = r; width = height = 2 * radius; } public double getRadius( ) {// добавленный метод return radius; } public double getSpace( ) {// метод интерфейса return radius * radius * Math.PI ; } public double getPerimeter( ) {// метод интерфейса return 2 * radius * Math.PI ; } public boolean isInside( double x, double y) {// метод интерфейса double dX = x – ( left + radius ) ; double dY = y – ( top + radius ) ; return ( (dX * dX + dY * dY)

Внутренние классы Внутренними (или вложенными) классами называются классы, определяемые внутри текста других классов. Существует 4 разновидности внутренних классов. 1.Статические внутренние классы (являются членами обрамляющего класса). 2.Просто внутренние классы ( - "" -). 3.Локальные классы (не являются членами обрамляющего класса). 4.Анонимные локальные классы ( - "" -). Во внутренних классах в свою очередь могут быть декларированы «еще более» внутренние классы. Имена всех классов в этой иерархии должны быть различными. При использовании внутренних классов возникает еще одно дерево отношений между классами, отражающее отношения между объектами. Наследование отражает отношение «являться» (окружность или прямоугольник являются геометрическими фигурами). Внутренние классы отражают отношение «быть частью» (двигатель или трансмиссия представляют собой части автомобиля). Для доступа к полям/методам обрамляющего класса нужно использовать синтаксис:.this.. Для доступа к затененным/замещенным полям родительских классов нужно, как обычно, использовать синтаксис:.super..

Статические внутренние классы-члены (и интерфейсы) Декларируются внутри основного класса наравне с полями и методами с использованием спецификатора static. Имеют доступ только к статическим полям/методам своего внешнего класса (и, естественно, к собственным членам), даже если они частные (private). Могут содержать статические поля, методы и классы, в отличие от других типов внутренних классов. Могут содержать нестатические поля и методы. Нестатические поля/методы возникают только при создании экземпляра этого класса. Пример статического внутреннего класса: class OuterClass( ) { public OuterClass( ) { }// конструктор private int outerField; static private int staticOuterField; static class InnerClass { int getOuterField( ) { return OuterClass.this.outerField; // здесь будет ошибка компиляции } int getStaticOuterField( ) { return OuterClass.staticOuterField; // здесь правильно } // из методов класса OuterClass можно вызывать // методы внутреннего InnerClassa }

Применение статических внутренних классов (1) Пусть, например, пишется программа, используемая в автоматизированных экзаменах. public class Question { private Type type; public Type getType() { return type; } public void setType(Type type) { this.type = type; } public static enum Type { SINGLE_CHOICE, MULIT_CHOICE, TEXT } В вопросе (класс Question) понятие "типа вопроса" (class Type) является очевидным. Другая альтернатива - создать класс верхнего уровня QuestionType - будет менее выразительной, по крайней мере в контексте класса Question. Клиентский код будет выглядеть, например, так: Question.Type type = question.getType(); if (type == Question.Type.TEXT) {... } Снаружи обращение к классу Type осуществляется через имя обрамляющего класса - Question.Type.

Применение статических внутренних классов (2) Еще одно интересное использование статических вложенных классов - тестирование приватных статических методов. Пример: public class ClassToTest { private static int internalMethod( … ) {// тестируемый метод... } public static class Test { public void testMethod() {... assert TEST_CONST_N == internalMethod( … ) : ERROR_CODE_N; … } Тестирование заключается в вызовах извне методов класса Test, имеющих вид: ClassToTest.Test.testMethod(); Тестируемый класс, очевидно, никак не использует свой внутренний статический класс. Результат компиляции программы будет содержать файлы: ClassToTest.class ClassToTest$Test.class После завершения тестирования и перед созданием jar-архива для распространения приложения все файлы с именами вида *$Test*.class могут быть удалены при выполнении build-скрипта. На работоспособность оставшихся файлов это никак не повлияет.

Нестатические внутренние классы-члены Декларируются внутри основного класса, ассоциируются с экземпляром этого класса. В отличие от статических внутренних классов-членов, имеют доступ к членам внешнего класса. Не могут содержать определения статических полей, методов и классов, но могут их наследовать. Пример: class OuterClass( ) { public OuterClass( ) { } private int outerField; class InnerClass { int getOuterField(){ return OuterClass.this.outerField;// здесь ошибки нет } Следует отметить, что нестатических внутренних интерфейсов быть не может. Объявление вида public class OuterClass { public interface ImNonStaticInterface {... } } будет интерпретироваться так: public class OuterClass { public static interface ImNonStaticInterface {... } }

Применение нестатических внутренних классов Пусть есть класс Users, используемый, например, для управления доступом пользователей к данным веб-сайта: public class Users {... public class Query { private Query( ) {... } public void setLogin( String login ) {... } public void setCreationDate( Date date ) {... } public List list( ) {... } public User single( ) {... } } public Query createQuery( ) { return new Query( ); } Внутренний класс Query предназначен для поиска пользователей по заданным параметрам. Класс Query может инкапсулировать в себе, например, работу с базой данных. При этом он имеет доступ к состоянию экземпляра своего внешнего класса Users. Пример клиентского кода: Users.Query query = users.createQuery( ); query.setCreationDate( currentDate ); List user = query.list( ); …

Локальные классы Локальные классы объявляются и используются внутри методов обрамляющего класса и, соответственно, не являются его членами. Вот пример: public class Handler { public void handle(String requestPath) {// обработка строки запроса в веб-сервере class Path {// класс специфической обработки строки List parts = new ArrayList (); String path = "/"; Path(String path) { if (path == null) return; this.path = path; for (String s : path.split("/")) if (s.trim().length() > 0) this.parts.add(s); } int size() { return parts.size(); } String get(int i) { return i > this.parts.size() - 1 ? null : this.parts.get(i); } boolean startsWith(String s) { return path.startsWith(s); } } Path path = new Path(requestPath); if (path.startsWith("/page")) { String pageId = path.get(1);... } if (path.startsWith("/post")) { String categoryId = path.get(1); String postId = path.get(2);... } }

Локальные классы Ограничения Как и классы-члены, локальные классы ассоциируются с экземпляром обрамляющего класса и имеют доступ ко всем его полям и методам. Кроме этого, локальный класс может обращаться к локальным переменным и параметрам метода, в котором он декларирован, но только если они объявлены с модификатором final. Причина состоит в том, что время жизни экземпляра локального класса может превышать время жизни метода, в котором он декларирован. Поэтому для него компилятором создаются его собственные копии всех тех переменных, с которыми экземпляр работает. А идентичность значения переменной и ее копии может быть обеспечена только в том случае, если она объявлена с модификатором final. На локальные классы есть ограничения: они видны только в пределах блока, в котором объявлены; они не могут быть объявлены как private, public, protected или static; они не могут иметь внутри себя статических объявлений (полей, методов, классов); исключением являются константы (static final); локальный класс не может называться точно так же, как обрамляющий его класс.

Анонимные классы Анонимные классы – это локальные классы без имени. Классический пример использования анонимного класса: new Thread( new Runnable() { public void run() {... // это тело анонимного класса } }).start(); // здесь не следует использовать \n перед ) Здесь на основании анонимного класса создается поток и запускается с помощью метода start класса Thread. Синтаксис создания анонимного класса состоит в использовании операции new с именем класса или интерфейса и телом создаваемого анонимного класса, заключенным в фигурные скобки. Если указано имя базового класса, то создаваемый анонимный класс будет его подклассом. Если указано имя интерфейса, то анонимный класс будет подклассом класса java.lang.Object и должен содержать реализацию всех методов интерфейса.

Анонимные классы Кроме всех ограничений, накладываемых на локальные классы, для анонимных классов есть еще одно: у такого класса не может быть явно определенных конструкторов (поскольку к него нет имени). Поэтому для анонимного класса компилятор строит конструктор по умолчанию без аргументов. Однако инициализаторы у таких классов могут быть. На этом основан следующий пример использования анонимного класса: import java.util.List; import java.util.ArrayList; public class Test { public static void main( String[ ] args ) { List values = new ArrayList ( ) {{// это блок-инициализатор add( "one« );// анонимного класса, add( "two« );// унаследовавшего все методы, add( "three« );// в том числе add от ArrayList }}; for (String value : values) { System.out.println(value); } } } one two three

Анонимные классы Анонимные классы имеет смысл использовать в тех случаях, когда: тело класса является очень коротким и нужен только один экземпляр класса и имя класса не важно и не облегчает понимание кода. Один из таких случаев типичен для обработки событий при реализации пользовательского интерфейса и используется очень широко. Пример: JFrame myFrame = new JFrame( ); myFrame.addWindowListener( new java.awt.event.WindowAdapter( ) { public void windowClosing( java.awt.event.WindowEvent evt ) { System.exit( 0 ); } }); Здесь создается анонимный класс, расширяющий класс-адаптер java.awt.event.WindowAdapter, реализующий как пустые все 7 методов интерфейса java.awt.event.WindowListener. Анонимый класс этого примера замещает пустой метод windowClosing собственной реализацией, наследуя все остальные пустые методы. При возникновении события «закрытие окна» будет вызван метод windowClosing анонимного класса, что приведет к завершению программы.

Массивы (1) Массивы в языке Java относятся к ссылочным типам и определяются способом, характерным для ссылочных типов. Полное определение включает три составных части. Объявление. Содержит имя типа элементов массива, сопровождаемое парами квадратных скобок, и имя переменной (скобки могут быть указаны при имени переменной): double[ ] currentBalance; String methodArgs[ ]; TailPosition[ ] tailPositions[ ]; // так объявлять массивы можно, но не рекомендуется float airPressure[ ][ ][ ]; Создание. Выполняется явно или неявно вызываемой операцией new, которая создает экземпляр специального класса, выделяет для него участок памяти нужного размера, инициализирует все элементы массива стандартным начальным значением для заданного типа элементов и возвращает указатель на экземпляр: currentBalance = new double[ 10 ]; methodArgs = new String[ argsCount ]; tailPosition = new TailPosition[ rows ][ columns ]; airPressure = new float[ domainWidth ][ domainHeight ][ 10 ]; Объявление и создание массивов могут быть объединены: double[ ] currentBalance = new double[ 10 ]; String[ ] methodArgs = new String[ argsCount ]; TailPosition[ ] tailPosition[ ] = new TailPosition[ rows ][ columns ]; float[ ][ ][ ] airPressure = new float[ domainWidth ][ domainHeight ][ 10 ];