Программирование мобильных телефонов на Java


Программирование мобильных телефонов

  • Введение
  • Глава 1. Устройство мобильных телефонов
  • Глава 2. Платформа Java 2 Micro Edition
  • Глава 3. Средства разработки мобильных приложений
  • Глава 4. Телефонные эмуляторы
  • Глава 5. Механизм работы приложений Java 2 ME
  • Глава 6. Классы пользовательского интерфейса
  • Глава 7. Программирование графики
  • Глава 8. Техника создания игр
  • Глава 9. Мобильная мультимедиа-библиотека
  • Заключение
  • Приложение 1. Основы языка Java
  • Приложение 2. Справочник по Java 2 Micro Edition

     

    Программирование на Java

  • Введение
  • Глава 1. Встроенные типы данных, операции над ними
  • Глава 2. Объектно-ориентированное программирование в Java
  • Глава 3. Пакеты и интерфейсы
  • Глава 4. Классы-оболочки
  • Глава 5. Работа со строками
  • Глава 6. Классы-коллекции
  • Глава 7. Классы-утилиты
  • Глава 8. Принципы построения графического интерфейса
  • Глава 9. Графические примитивы
  • Глава 10. Основные компоненты
  • Глава 11. Размещение компонентов
  • Глава 12. Обработка событий
  • Глава 13. Создание меню
  • Глава 14. Апплеты
  • Глава 15. Изображения и звук
  • Глава 16. Обработка исключительных ситуаций
  • Глава 17. Подпроцессы
  • Глава 18. Потоки ввода/вывода
  • Глава 19. Сетевые средства Java
  • Приложение. Развитие Java

  • 

    3fb01dc2


     

     

    Интерфейсы

    Вы уже заметили, что получить расширение можно только от одного класса, каждый класс в или с происходит из неполной семьи, как показано на рис. 3.4, а. Все классы происходят только от "Адама", от класса object . Но часто возникает необходимость породить класс о от двух классов вис, как показано на рис. 3.4, б. Это называется множественным наследованием (multiple inheritance). В множественном наследовании нет ничего плохого. Трудности возникают, если классы вис сами порождены от одного класса А, как показано на рис. 3.4* в. Это так называемое "ромбовидное" наследование.

    Рис. 3.4. Разные варианты  наследования

    В самом деле, пусть в классе А определен метод f (), к которому мы обращаемся из некоего метода класса о. Можем мы быть уверены, что метод f о выполняет то, что написано в классе А, т. е. это метод A.f о? Может, он переопределен в классах в и с? Если так, то каким вариантом мы пользуемся: B.f() или c.f()? Конечно, можно определить экземпляры классов и обращаться к методам этих экземпляров, но это совсем другой разговор.

    В разных языках программирования этот вопрос решается по-разному, главным образом, уточнением имени метода ft). Но при этом всегда нарушается принцип KISS. Вокруг множественного наследования всегда много споров, есть его ярые приверженцы и столь же ярые противники. Не будем встревать в эти споры, наше дело — наилучшим образом использовать средства языка для решения своих задач.

    Создатели языка Java после долгих споров и размышлений поступили радикально — запретили множественное наследование вообще. При расширении класса после слова extends можно написать только одно имя суперкласса. С помощью уточнения super можно обратиться только к членам непосредственного суперкласса.

    Но что делать, если все-таки при порождении надо использовать несколько предков? Например, у нас есть общий класс автомобилей Automobile , от которого можно породить класс грузовиков Truck и класс легковых автомобилей Саг. Но вот надо описать пикап Pickup . Этот класс должен наследовать свойства и грузовых, и легковых автомобилей.

    В таких случаях используется еще одна конструкция языка Java— интерфейс. Внимательно проанализировав ромбовидное наследование, теоретики ООП выяснили, что проблему создает только реализация методов, а не их описание.

    Интерфейс (interface), в отличие от класса, содержит только константы  и заголовки методов, без их реализации.

    Интерфейсы размещаются в тех же пакетах и подпакетах, что и классы, и компилируются тоже в class-файлы.

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

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

    Затем, в фигурных скобках, записываются в любом порядке константы и заголовки методов. Можно сказать, что в интерфейсе все методы абстрактные, но слово abstract писать не надо. Константы всегда статические, но слова static и final указывать не нужно.

    Все константы и методы в интерфейсах всегда открыты, не надо даже .указывать модификатор public .

    Вот какую схему можно предложить для иерархии автомобилей:

    interface Automobile{ . . . }

    interface Car extends Automobile{ . . . }

    interface Truck extends Automobile{ . . . } 

    interface Pickup extends Car, Truck{ . . . }

    Таким образом, интерфейс — это только набросок, эскиз. В нем указано, что делать, но не указано, как это делать.

    Как же использовать интерфейс, если он полностью абстрактен, в нем нет ни одного полного метода?

    Использовать нужно не интерфейс, а его реализацию (implementation). Реализация интерфейса — это класс, в котором расписываются методы одного или нескольких интерфейсов. В заголовке класса после его имени или после имени его суперкласса, если он есть, записывается слово implements и, через запятую, перечисляются имена интерфейсов.

    Вот как можно реализовать иерархию автомобилей:

    interface Automobile{ . . . }

    interface Car extends Automobile! . . . }

    class Truck implements Automobile! . . . }

    class Pickup extends Truck implements Car{ . . . }

    или так:

    interface Automobile{ . . . } 

    interface Car extends Automobile{ . . . } 

    interface Truck extends Automobile{ . . . } 

    class Pickup implements Car, Truck{ . . . }

    Реализация интерфейса может быть неполной, некоторые методы интерфейса расписаны, а другие — нет. Такая реализация — абстрактный класс, его обязательно надо пометить модификатором abstract .

    Как реализовать в классе pickup метод f() , описанный и в интерфейсе саг, и в интерфейсе Truck с одинаковой сигнатурой? Ответ простой — никак. Такую ситуацию нельзя реализовать в классе Pickup . Программу надо спроектировать по-другому.

    Итак, интерфейсы позволяют реализовать средствами Java чистое объектно-ориентированное проектирование, не отвлекаясь на вопросы реализации проекта.

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

    Интересно то, что мы можем создавать ссылки на интерфейсы. Конечно, указывать такая ссылка может только на какую-нибудь реализацию интерфейса. Тем самым мы получаем еще один способ организации полиморфизма.

    Листинг 3.3 показывает, как можно собрать с помощью интерфейса хор домашних животных из листинга 2.2.

    Листинг 3.3. Использование интерфейса для организации полиморфизма

    interface Voice{

    void voice(); 

    }

    class Dog implements Voice{

      public void voice (){

        System.out.println("Gav-gav!");

      } 

    }

    class Cat implements Voice{

      public void voice (){

        System.out.println("Miaou!");

      } 

    }

    class Cow implements Voice{ 

      public void voice(){

        System.out.println("Mu-u-u!"); 

      }

    }

    public class Chorus{

      public static void main(String[] args){ 

        Voiced singer = new Voice[3]; 

        singer[0] = new Dog(); 

        singer[1] = new Cat(); 

        singer[2] = new Cow(); 

        for(int i = 0; i < singer.length; i++)

          singer[i].voice();

      }

    }

    Здесь используется интерфейс voice вместо абстрактного класса Pet , описанного в листинге 2.2.

    Что же лучше использовать: абстрактный класс или интерфейс? На этот вопрос нет однозначного ответа.

    Создавая абстрактный класс, вы волей-неволей погружаете его в иерархию классов, связанную условиями одиночного наследования и единым предком — классом object . Пользуясь интерфейсами, вы можете свободно проектировать систему, не задумываясь об этих ограничениях.

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

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

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

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

    Листинг 3.4. Система управления светофором

    interface Lights{

      int RED    = 0;

      int YELLOW = 1;

      int GREEN  = 2;

      int ERROR  = -1; 

    class Timer implements Lights{

      private int delay;

      private static int light = RED;

      Timer(int sec)(delay = 1000 * sec;}

      public int shift(){

        int count = (light++) % 3; 

        try{

          switch(count){

            case RED: Thread.sleep(delay); break; 

            case YELLOW: Thread.sleep(delay/3); break; 

            case GREEN: Thread.sleep(delay/2); break; 

          }

        }catch(Exception e){return ERROR;} 

        return count;

      }

    }

    class TrafficRegulator{

      private static Timer t = new Timer(1);

      public static void main(String[] args){

        for (int k = -0; k < 10; k++)

          switch(t.shift()){

          case Lights.RED:    System.out.println("Stop!"); break; 

          case Lights.YELLOW: System.out.println("Wait!"); break; 

          case Lights.GREEN:  System.out.println("Go!");   break; 

          case Lights.ERROR:  System.err.println("Time Error"); break; 

          default: System.err.println("Unknown light."); return;

        }

      }

    }

    Здесь, в интерфейсе Lights , определены константы, общие для всего проекта.

    Класс Timer реализует этот интерфейс и использует константы напрямую как свои собственные. Метод shift о этого класса подает сигналы переключения светофору с разной задержкой в зависимости от цвета. Задержку осуществляет метод sleep() класса Thread из стандартной библиотеки, которому передается время задержки в миллисекундах. Этот метод нуждается в обработке исключений try{} catch() {} , о которой мы будем говорить в главе 16.

    Класс TrafficReguiator не реализует интерфейс Lights и пользуется полными именами Lights.RED и т.д. Это возможно потому, что константы RED, YELLOW и GREEN по умолчанию являются статическими.

    Теперь нам известны все средства языка Java, позволяющие проектировать решение поставленной задачи. Заканчивая разговор о проектировании, нельзя не упомянуть о постоянно пополняемой коллекции образцов проектирования (design patterns).

     

    -
    



    Copyright © vzlom-1.ru 2020-2023