М. Бен-Ари Языки программирования. Практический сравнительный анализ. Предисловие

Вид материалаДокументы

Содержание


Сравнение с другими языками
18.7. Библиотеки Java
Подведем итог
The Java Language Specification.
Где получить компиляторы
Mathematical Theory of Computation.
Eiffel: the Language.
Ada 95Reference Manual.
The Annotated C++ Reference Manual.
The Design and Evolution of C++.
Подобный материал:
1   ...   10   11   12   13   14   15   16   17   18

18.4. Полиморфные структуры данных


В языках Ada и C++ есть два пути построения полиморфных структур данных: generics в Ada и templates в C++ для полиморфизма на этапе компиляции, и типы в Ada и указатели/ссылки на классы для полиморфизма на CW-этапе выполнения. Преимущество generies/templates состоит в том, что структура данных фиксируется при создании экземпляра во время компиляции; это позволяет как генерировать более эффективный код, так и более экономно распределять память для структур данных.

В языке Java решено реализовать полиморфизм только на этапе выполне­ния. Как и в языках Smalltalk и Eiffel, считается, что каждый класс в Java по­рождается из корневого класса, названного Object. Это означает, что значение любого непримитивного типа8 может быть присвоено объекту типа Object. (Конечно, это работает благодаря семантике ссылки.)

Чтобы создать связанный список, класс Node должен быть сначала опре­делен как содержащий (указатель на) Object. Класс списка тогда должен со­держать методы вставки и поиска значения типа Object:



Java
class Node {

Object data;

Node next;

}

class List {


Java
private Node head;

void Put(Object data) {...};

Object Get() {...};

}


Если L является объектом типа List (Список), и а является объектом типа Airplane_Data, то допустимо L.Put(a), потому что Airplane_Data порождено из Object. Когда значение выбирается из списка, оно должно быть приведено к соответствующему потомку Object:



Java
а = (Airplane_Data) List.Get();


Конечно, если возвращенное значение не имеет тип Airplane_Data (или не по­рождено из этого типа), возникнет исключение.

Преимущество этой парадигмы состоит в том, что в Java очень просто писать обобщенные структуры данных, но по сравнению с generics/tem-plates имеются два недостатка: 1) дополнительные издержки семантики ссылки (даже для списка целых чисел!), и 2) опасность, что объект, разме­щенный не в той очереди, приведет при поиске к ошибке на этапе выполне­ния программы.


18.5. Инкапсуляция


В разделе 13.1 мы обсуждали тот факт, что в языке С нет специальной конст­рукции инкапсуляции, а в разделе 13.5 отметили, что операция разрешения области действия и конструкция namespace (пространство имен) в C++ уточ­няет грубое приближение языка С к проблеме видимости глобальных имен. Для совместимости в язык C++ также не была включена конструкция инкап­суляции; вместо этого сохраняется зависимость от «h»-файлов. В Ada есть конструкция пакета, которая поддерживает инкапсуляцию конструкций в мо­дули (см. раздел 13.3), причем спецификации пакетов и их реализации (тела) могут компилироваться отдельно. Конструкции with позволяют разработчику программного обеспечения точно определить зависимости между модулями и использовать порожденные пакеты (кратко упомянутые в разделе 15.2) для разработки модульных структур с иерархической достижимостью.

Java содержит конструкцию инкапсуляции, названную пакетом (package), но, к сожалению, конструкция эта по духу ближе к пространству имен (names­pace) в языке C++, чем к пакету Ada! Пакет является совокупностью классов:


package Airplane_Package;

public class Airplane_Data


Java
{

int speed; // Доступно в пакете

private int mach_speed; // Доступно в классе

public void set_speed(int s) {...}; // Глобально доступно

public int get_speed() {...};

}

public class Airplane_Database

{

public void new_airplane(Airplane_Data a, int i)

{

if (a.speed > 1000) // Верно !

a.speed = a.mach_speed; // Ошибка !

}

private Airplane_Data[ ] database = new Airplane_Data[1000];

}


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

Спецификаторы public и private аналогичны принятым в языке C++: pub­lic (общий) означает, что элемент доступен за пределами класса, в то время как private (приватный) ограничивает достижимость для других членов класса. Ес­ли никакой спецификатор не задан, то элемент видим внутри пакета. В приме­ре мы видим, что элемент int speed (скорость) класса Airplane_Data не имеет никакого спецификатора, поэтому к нему может обратиться оператор внутри класса Airplane_Database, так как два класса находятся в одном и том же паке­те. Элемент mach_speed объявлен как private, поэтому он доступен только внутри класса Airplane_Data, в котором он объявлен.

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

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


Сравнение с другими языками


Пакеты Java служат для управления глобальными именами и достижимостью аналогично конструкции namespace в языке C++. При заданных в нашем примере объявлениях любая Java-программа может содержать:



Java
Airplane_Package.Airplane_Data a;

a.set_speed(100);


потому что имена класса и метода объявлены как public. He изучив полный исходный текст пакета, нельзя узнать, какие именно классы импортированы. Есть оператор import, который открывает пространство имен пакета, разре­шая прямую видимость. Эта конструкция аналогична конструкциям using в C++ и uses Ada.

Основное различие между Java и Ada состоит в том, что в Ada специфика­ция пакета и тело пакета разделены. Это не только удобно для сокращения размера компиляций, но и является существенным фактором в разработке и поддержке больших программных систем. Спецификация пакета может быть заморожена, позволяя параллельно разрабатывать тело пакета и вести разработку других частей. В Java «интерфейс» пакета является просто совокупно­стью всех public-объявлений. Разработка больших систем на Java требует, что­бы программные инструментальные средства извлекали спецификации паке­та и гарантировали совместимость спецификации и реализации.

Конструкция пакета дает Java одно главное преимущество перед C++. Пакеты сами используют соглашение об иерархических именах, которое позволяет компилятору и интерпретатору автоматически размещать клас­сы. Например, стандартная библиотека содержит функцию, названную Java.lang.String.toUpperCase. Это интерпретируется точно так же, как опе­рационная система интерпретирует расширенное имя файла: toUpperCase является функцией в пакете Java.lang.String. Библиотеки Java могут (но не обязаны) быть реализованы как иерархические каталоги, где каждая функ­ция является файлом в каталоге для своего класса. Отметим, что иерархи­чность имен как бы вне языка; подпакет не имеет никаких особых привиле­гий при доступе к своему родителю. Здесь мы видим четкое отличие от пакетов-детей в Ada, которые имеют доступ к private-декларациям своих родителей при соблюдении правил, которые не позволяют экспортировать эти декларации.


18.6. Параллелизм


Ada — один из немногих языков, в которых поддержка параллелизма включе­на в сам язык, в отличие от делегирования этих функций операционной сис­теме. Язык Java продолжает идеологию языка Ada в отношении пере­носимости параллельных программ вне зависимости от операционных сис­тем. Важное применение параллелизма в Java — программирование серверов: каждый запрос клиента заставляет сервер порождать (spawn) новый процесс для выполнения этого запроса.

Параллельно исполняемые конструкции Java называются нитями (thread). Собственно в параллелизме нет никаких существенных различий между нитью и тем, что называют стандартным термином процесс; отличие состоит в реализации, ориентированной на выполнение многих нитей внутри одного и того же адресного пространства. Для разработки и анализа параллельных ал­горитмов используется та же самая модель, что и в гл. 12 ,— чередующееся вы­полнение атомарных инструкций процессов.

Класс Java, который наследуется из класса Thread, объявляет только но­вый тип нить. Чтобы реально создать нить, нужно запросить память, а затем вызвать функцию start. В результате будет запущен метод run внутри нити:


class My_Thread extends Thread

{

public void run(){...}; // Вызывается функцией start


Java
}

My_Thread t = new My_Thread(); // Создать нить

t.start(); //Активизировать нить


Одна нить может явно создавать, уничтожать, блокировать и разблокировать другую нить.

Эти конструкции аналогичны конструкциям в языке Ada, которые позво­ляют определить тип task (задача) и затем динамически создавать задачи.


Синхронизация


Java поддерживает форму синхронизации аналогично мониторам (см. раздел 12.4). Класс может содержать методы, специфицированные как synchronized (синхронный). С каждым таким объектом в классе связывается блокировка-пропускник (lock), которая гарантирует, что только одна нить в данный момент может выполнять синхронный метод в объекте. Следующий пример показывает, как объявить монитор для защиты разделяемого ресурса от одно­временного использования несколькими нитями:


class Monitor

{

synchronized public void seize() throws InterruptedException

{

while (busy) wait();


Java
busy = true;

}

synchronized public void release()

{

busy = false;

notify();

}

private boolean busy - false

}


Монитор использует булеву переменную, которая указывает состояние ресур­са. Если две нити пытаются выполнить метод seize в мониторе, то только одна из них пройдет пропускник и выполнится. Эта нить установит переменную busy (занято) в состояние true (истина) и перейдет к использованию ресурса. По завершении метода пропускник откроется, и другая нить сможет выполнить метод seize. Теперь, однако, переменная busy будет иметь значение false (ложь). Вместо ненужных затрат времени ЦП на непрерывную проверку пере­менной нить предпочитает освободить ЦП с помощью запроса wait (ждать). Когда первая нить заканчивает использование разделяемого ресурса, она вы­зывает notify (уведомление), которое позволит ожидающей нити снова возоб­новить выполнение синхронного метода.

Конструкции Java для параллелизма достаточно просты. Нет ничего по­хожего на сложные рандеву Ada для прямой связи процесс-процесс. Даже по сравнению с защищенными объектами конструкции Java относительно слабы:


• Барьер защищенного объекта автоматически перевычисляется всякий раз, когда его значение может измениться; в Java нужно явно программи­ровать циклы.


• Java предоставляет простую блокировку-пропускник для каждого объек­та; в Ada заводится очередь для каждого входа. Таким образом, если не­сколько нитей Java ожидают входа в синхронный объект, вы не можете знать, какой из них будет запущен по notity, поэтому трудно программи­ровать алгоритмы с гарантированно ограниченным временем ожидания.


18.7. Библиотеки Java


В языках программирования очевидна тенденция сокращения «размеров» языка за счет расширения функциональности библиотек. Например, write — это оператор в языке Pascal со специальным синтаксисом, тогда как в Ada нет никаких операторов ввода/вывода; вместо этого ввод/вывод поддерживается пакетами стандартной библиотеки.

Стандартные библиотеки Ada предоставляют средства для ввода/вывода, обработки символов и строк, для вычисления математических функций и для системных интерфейсов. Язык C++ также поддерживает контейнерные клас­сы, такие как стеки и очереди. Точно так же Java содержит базисные библио­теки, названные java.lang, java.util и java.io, которые являются частью специ­фикации языка.

В дополнение к спецификации языка имеется спецификация для интер­фейса прикладного программирования (Application Programming Interface — API), который поддерживают все реализации Java. API состоит из трех библи­отек: Java.applet, Java.awt и java.net.

Java.applet поддерживает создание и выполнение аплетов и создание при­кладных программ мультимедиа.

Абстрактный комплект инструментальных оконных средств (Abstract Window Toolkit — AWT) — это библиотека для создания графических интер­фейсов пользователя (GUI): окна, диалоговые окна и растровая графика.

Библиотека для сетевой связи (java.net) обеспечивает необходимый интер­фейс для размещения и пересылки данных по сети.


Подведем итог:


• Java — переносимый объектно-ориентированный язык с семантикой ссылки.


• Интерфейс прикладного программирования API представляет переноси­мые библиотеки для поддержки развития программного обеспечения в сетях.


• Защита данных и безопасность встроены в язык и модель.


• Многие важные концепции Java заложены в языково-независимую ма­шину JVM.


8.8. Упражнения


1. Задано арифметическое выражение:


(а + b) * (с + d)


Java определяет, что оно должно вычисляться слева направо, в то время как C++ и Ada позволяют компилятору вычислять подвыражения в любом порядке. Почему в Java более строгая спецификация?


2. Сравните конструкцию final в Java с константами в Ada.


3. Каково соотношение между спецификатором friend в C++ и конструк­цией пакета в Java.


4. C++ использует спецификатор protected (защищенный), чтобы разре­шить видимость членов в порожденных классах. Как именно конструк­ция пакета влияет на понятие защищенности в Java?


5. Сравните интерфейс в Java с многократным наследованием в C++.


6. Проанализируйте различия между пространством имен (namespace) в C++ и пакетом в Java, особенно относительно правил, касающихся фай­лов и вложенности.

7. Конструкция исключения в Java совершенно аналогична конструкции исключения в C++. Одно важное различие состоит в том, что метод Java должен объявить все исключения, которые он может породить. Обос­нуйте это проектное решение и обсудите его последствия.

  1. Сравните мониторы Java с классической конструкцией монитора.


9. Сравните возможности обработки строк в Ada95, C++ и Java.


10. Сравните операции clone и eguals в Java с этими операциями в языке Eiffel.


Ссылки


Официальное описание языка дается в:

James Gosling, Bill Joy and Guy Steele. The Java Language Specification. Addison-Wesley, 1997.

Sun Microsystems, Inc., где разработан язык Java, имеет Web-сайт, содержа­щий документацию и программное обеспечение: ссылка скрыта.


Приложение А


Где получить компиляторы


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

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

Чтобы получать информацию относительно компиляторов для какого-ли­бо языка, просмотрите файлы, называющиеся FAQ (Freguently Asked Questions — часто задаваемые вопросы). Их можно загрузить через аноним­ный ftp по адресу rtfm.mit.edu. В директории /pub/usenet находится (очень длинный) список поддиректорий; просмотрите comp.lang.x, где х — одно из ada, apl, с, с++, eiffel, icon, lisp, ml, prolog, Smalltalk и т.д. Переходите в одну и; этих поддиректорий и загружайте файлы, в которых есть символы FAQ. Эта файлы будут содержать списки доступных компиляторов языка, в частности те, которые можно загрузить, используя ftp. Хотя эти программы не представ­ляют интереса как коммерческие пакеты, они вполне подходят для изучение и экспериментирования с языком.

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


C++

Свободно распространяемый компилятор для языка C++, называющийся дсс, был разработан в рамках проекта GNU компании Free Software Foundation. Более подробно см. FAQ в поддиректории gnu.g++.help на узле rtfm.mit.edu. Компилятор дсс был перенесен на большинство компьютеров включая персональные. Так как язык C++ все еще стандартизуется, дсс мо­жет отличаться от других компиляторов C++.


Ada 95


Нью-Йоркский университет разработал свободно распространяемый компи­лятор для языка Ada 95, названный gnat (GNU Ada Translator), gnat использу­ет выходную часть дсс и перенесен почти на все компьютеры, которые поддер­живают дсс. Современную информацию относительно ftp-сайтов для gnat см. в Ada FAQ; главный сайт находится в директории /pub/gnat в cs.nyu.edu. Там вы также найдете управляемую с помощью меню среду программирования для gnat, которая была разработана Университетом Джорджа Вашингтона.


AdaS


Pascal- S — это компилятор для подмножества языка Pascal, который выраба­тывает Р-код, являющийся машинным кодом для искусственной стековой ма­шины. Включен также интерпретатор для Р-кода. Его автор разработал вер­сию Pascal-S, названную AdaS, которая компилирует небольшое подмножест­во языка Ada. Исходный код AdaS можно найти в файле adasnn.zip (где пп — номер версии) в каталоге /languages/ada/crsware/pcdp в общей библиотеке языка Ada (PAL) на хосте wuarchive.wustl.edu.

AdaS не годится для серьезного программирования, но это превосходный инструмент для изучения методов реализации конструкций языка програм­мирования, в частности управления стеком при вызовах подпрограмм и воз­вратах из них.


Приложение Б


Библиографический список


Обзоры по языкам программирования можно найти в:

Ellis Horowitz (ed.). Programming Languages: 4 Grand Tour. Springer Verlag, 1983.

Jean E. Sammet. Programming Languages: History and Fundamentals. Prentice Hall, 1969.

Richard L. Wexelblat. History of Programming Languages. Academic Press, 1981.

Особенно интересна книга Векселблата (Wexelblat); это запись конферен­ции, где разработчики первых языков программирования описывают истоки и цели своей работы.

Превосходное введение в теорию вычисления (логику, машины Тьюринга, формальные языки и верификацию программ) можно найти в:

Zohar Manna. Mathematical Theory of Computation. McGraw-Hill, 1974.

В учебниках, рассчитанных на подготовленных учащихся обсуждается формальная семантика языков программирования:

Michael Marcotty and Henry Legrand. Programming Language Landscape: Syntax, Semantics and Implementation. SRA, Chicago, 1986.

Bertrand Meyer. Introduction to the Theory of Programming Languages. Prentice Hall International, 1991.

По компиляции смотрите следующие работы:

Alfred Aho, Ravi Sethi and Jeffrey D. Ullman. Compilers: Principles, Techniques and Tools. Addison-Wesley, 1986.

Charles N. Fisher and Richard J. LeBlanc. Grafting a Compiler. Benjamin Cummings, 1988.

Хорошим введением в объектно-ориентированное проектирование и про­граммирование является:

Bertrand Meyer. Object-oriented Software Construction. Prentice Hall Inter­national, 1988.

Обратите внимание, что описанная там версия языка Eiffel устарела; если вы хотите изучить язык, смотрите современное описание:

Bertrand Meyer. Eiffel: the Language. Prentice Hall, 1992.


Конкретные языки программирования


Мы даже не будем пытаться перечислить множество учебников по языкам С, Ada и C++! Формальное описание языка Ada можно найти в справочном руководстве:

Ada 95Reference Manual. ANSI/ISO/IEC-8652:1995.

Справочное руководство очень формальное и требует тщательного изуче­ния. Существует сопутствующий документ, называемый Объяснением (Ratio­nale), в котором описана мотивация языковых конструкций и даны обширные примеры. Файлы, содержащие текст этих документов, можно бесплатно за­грузить, как описано в Ada FAQ.

Стандарт языка С — ANS ХЗ.159-1989; международный стандарт — ISO/IEC 9899:1990. В настоящее время (конец 1995 г.), язык C++ еще не стан­дартизирован; информацию о том, как получить последний предлагаемый вари­ант стандарта языка C++, см. в FAQ. Более доступно справочное руководство:

Margaret A. Ellis and Bjarne Stroustrup. The Annotated C++ Reference Manual. Addison-Wesley, 1990 (reprinted 1994).

Следующая книга является «обоснованием» языка C++ и должна быть прочитана всеми серьезными студентами, изучающими этот язык:

Bjarne Stroustrup. The Design and Evolution of C++. Addison-Wesley, 1994.

Другие широко используемые объектно-ориентированные языки, кото­рые стоит изучить, — Smalltalk и CLOS:

Adele Goldberg and David Robson. Smalltalk-80, the Language and its Implementation. Addison-Wesley, 1983.

Sonya E. Keene. Object-Oriented Programming in Common Lisp: a Programmer's Guide. Addison-Wesley, 1989.

В разделе 1.3 мы рекомендовали вам изучить один или несколько языков, основанных на конкретной структуре данных. Следующий список позволит вам начать это изучение:

Leonard Oilman and Alien J. Rose. APL: An Interactive Approach. John Wiley, 1984*.

Ralph E. Griswold and Madge T. Griswold. The Icon Programming Language (2nd Ed.). Prentice Hall, 1990.

J.T. Schwartz, R.B.K. Dewar, E. Dubinsky, and E. Schonberg. Programming with Sets: An Introduction to SETL Springer Verlag, 1986.

Patrick H.Winston and Berthold K.P.Horn. LISP (3rd Ed.). Addjson-Wesley, 1989.

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

М. Ben-Ari. Principles of Concurrent and Distributed Programming. Prentice Hall International, 1990.

Ivan Bratko. Prolog Programming for Artificial Intelligence (2nd Ed.). Addison-Wesley, 1990.

Chris Reade. Elements of Functional Programming. Addison-Wesley, 1989. Leon SterUng and Ehud Shapiro. The Art of Prolog. MIT Press, 1986. Jeffrey D. Ullman. Elements of ML Programming. Prentice Hall, 1994.