Думаю, что тот, кто ввел понятие интерфейса, возможно и не подозревал, какое фантастическое по своим возможностям сотворил явление. Хотя это только мои догадки. В любом случае понятие интерфейса раздвинуло возможности ООП весьма сильно.
Так что же такое интерфейс ? По сути — это описание голой функциональности без каких либо привязок к особенностям класса. Если выражаться немного образно, то классы получили возможность иметь профессии — отправитель почты, управляющий транзакциями, распределитель страниц, контроллер и т.д. Я сейчас пытаюсь обрушить на вас грандиозность этой идеи и понимаю, что пока не получается. Просто поверьте на слово — это здорово. С помощью интерфейсов отношения между объектами становятся более гибкими, что позволяет строить архитектуру приложений из еще более независимых блоков.
Если опять вернуться к аналогии профессии — по сути вас не волнует пол, цвет глаз, возраст и рост человека, который работает водителем, электриком или программистом. Вам важно, что он умеет делать эту работу и умеет делать ее хорошо (в какой-то степени). Что еще важно отметить — как человек может обладать несколькими профессиями, так и класс может реализовывать несколько интерфейсов. Если вернуться к теме наследования, то как известно класс может наследоваться ТОЛЬКО ОТ ОДНОГО класса. А вот интерфейсов у него может быть достаточно много.

Перейдем от слов к делу — вернемся к нашему (широко известному в узких кругах) классу Robot 🙂
В прошлый раз мы научили его двигаться и запоминать свой маршрут для отображения на форме. При его создании мы немного забежали вперед — я ввел класс, о котором мы еще некоторое время не будем говорить — ArrayList. Пока мы не будем его обсуждать — просто еще раз отмечу, что это класс позволяет вам работать с динамическими списками объектов — добавлять, удалять, просматривать, перебирать. Расширим возможности нашего робота — наделим его способностью сообщать кому-нибудь о том, что он начал двигаться вперед и остановился. Возможно, что не такая уж и бесполезная вещь — например при наблюдении за марсоходом.
Заострим свое внимание на словах «сообщать кому-нибудь». Это очень тонкий момент — роботу ведь действительно неважно, кому сообщать. Вот она, точка применения интерфейса — нам неважно кто будет слушать — нам важно, чтобы этот кто-то или что-то умело слушать наше сообщение, соблюдало определенный контракт. Пришло время посмотреть, как определяется интерфейс.

Как видите, описание интерфейса достаточно несложный процесс — гораздо сложнее понять, когда он действительно нужен. Рассмотрим его несколько подробнее.
Во-первых, для описания интерфейса надо использовать слово interface. Во-вторых, методы не содержат тела — совсем. Это просто запрещено правилами. Создается только описание — доступность, возвращаемый тип и входные параметры. После этого ставится точка с запятой.
У вас может возникнуть вопрос — а зачем мы передаем координаты x и y в методы интерфейса ? Вполне резонный вопрос, но и вполне резонный ответ — робот же должен сообщить где он стартовал и где остановился.
Настало время модифицировать код робота для того, чтобы он, во-первых, мог зарегистрировать «слушателя», а во-вторых он должен с ним уметь работать. Смотрим код.

В нашем коде есть три момента, на которые надо обратить внимание. Первое — это объявление ссылки на слушателя.

private RobotListener listener;

Да-да, это то самое решение для отношений между объектами, которое мы обсуждали в разделе Отношения между классами. И опять никакого чуда не произошло — мы должны сказать роботу, кому он должен посылать сообщения. Второе — для установки слушателя нам потребуется метод

И наконец третье — в методе forward робот вызывает слушателя. Обратите внимание — перед вызовом мы делаем проверку на NULL — если слушатель не установлен, то мы можем получить неприятную ошибку NullPointer — указатель пустой. Такие ошибки делают и достаточно опытные программисты — так что будьте внимательны. Очень коварная ошибка. Хотя достаточно простая при обнаружении.
Наш робот готов посылать события и наша задача теперь создать этого самого слушателя. И теперь ВНИМАНИЕ — вы не можете создавать объект типа интерфейс. Очень похоже на абстрактный класс, но если в абстрактном классе хоть какой-то код может присутствовать, то в интерфейсе его вообще нет. Если опять вернуться к аналогии профессии, то наличие врача в клинике означает присутствие человека, который обладает профессией врача. С интерфейсами дело обстоит точно также — мы должны создать класс, который реализует (имплементирует — implements) нужный интерфейс. И опять же по аналогии с профессией — класс может реализовать более одного интерфейса. Сделаем простого слушателя:

Как видите, форма записи достаточно несложная — если вы хотите сказать, что класс реализует интерфейс вы пишите слово implements и после него указываете нужный интерфейс. Если надо реализовать несколько интерфейсов, то они пишутся через запятую — например так

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

Надеюсь, вы помните, что значит аннотация @Override — мы это уже обсуждали. Чтобы не бегать по ссылкам — это специальное обозначение того, что метод переопределен.
Слушатель готов — осталось только подключить его к нашему роботу и запустить программу. Подключение делается через вызов метода setListener. Можем это сделать в классе RobotManager.

В коде мы создаем экземпляр объекта SimpleRobotListener, который реализует интерфейс RobotListener и класс Robot без проблем позволяет установить себе слушателя. Отметим важную мысль — классу Robot совершенно неважно, какой класс реализует нужный интерфейс.
Если быть еще более точным, то при создании SimpleRobotListener можно (даже нужно) писать так:

Можно вспомнить про полиморфизм — класс умеет быть слушателем робота (профессия у него такая). И мы работаем с экземпляром класса SimpleRobotListener как с профессией RobotListener. Такое абстрагирование весьма удобно при проектировании — вы это увидите. Вы не привязываетесь к конкретному классы — вы привязываетесь исключительно к «профессии».
Теперь при запуске нашей программы в консоль вывода будет виден текст, который должен выводить наш слушатель.

Исходный код программы можно скачать здесь — Robot5

Свойства и константы

Т.к. интерфейс является исключительно описанием «что делать», но никогда не содержит «как делать» (как вы уже видели, методы не содержат реализацию), то интерфейс не может включать свойства — их просто негде вызывать. Из этого правила есть одно исключение — интерфейс может иметь константы. Что-то вроде этого

К полю NAME можно обращаться как к константе. Думаю, что здесь все достаточно очевидно и понятно — в интерфейсе можно описать константы. Что бывает востребовано.

Двигаем квадрат

Рассмотрим пример, который позволит нам создать интерактивное графическое приложение, в котором мы будем использовать интерфейсы. Я очень тепло отношусь к примерам, которые позволяют наглядно посмотреть работу программы. И графические приложения являются крайне благодарным материалом. Итак, наша задача — создать приложение, которое в помощью кнопок будет передвигать по экрану квадрат.
На форме будет две кнопки — UP и DOWN, которые позволят двигать квадрат соответственно вверх и вниз. Само приложение не сложное — здесь важно увидеть применение интерфейсов.
Кнопки на самом деле очень похожи по своей идее на нашего робота — при нажатии на них они способны рассылать события тем объектам, которые у них зарегистрированы как слушатели. Причем кнопкам позволяется иметь много слушателей одновременно — целый список.
Приведем код формы, которая содержит три компонента — две кнопки для управления и панель, которая умеет рисовать квадрат с определенными координатами и, что важно отметить сразу, умеет «слушать» события от кнопок. Умение слушать достигается очень просто — наша панель реализует интерфейс ActionListener (этот интерфейс описан в библиотеке Swing). Причем компонент умеет слушать события от обеих кнопок — он зарегистрирован в качестве слушателя у обеих. Итак, смотрим код:

Если просто аккуратно прочитать код, то видно, что сначала мы создаем панель, а потом создаем кнопки. При создании кнопки мы даем ее заголовок (прямо в конструкторе), потом устанавливаем ей название команды (для того, чтобы панель могла различать, кто ее позвал — кнопка UP или DOWN). Вызывая метод кнопки addActionListener мы регистрируем нашу панель в качестве слушателя. И в самом конце устанавливаем нашу кнопку либо вверх, либо вниз. Мы уже касались вопроса о layout в разделе Полиморфизм — так вот по умолчанию форма использует BorderLayout — здесь компоненты распределяются по сторонам света — север, юг, запад, восток и в центре. Если не указывать направление, то компонент располагается в центре — дальше догадаетесь.
Теперь посмотрим код нашей панели — он не должен показаться вам очень сложным — метод прорисовки мы уже использовали неоднократно.

Новинкой для нас будет метод actionPerformed. Это метод, который описан в интерфейсе ActionListener. И кнопке совершенно безразлично, какой именно класс ее слушает — это может быть другой компонент, класс для записи файлов, для отсылки почты и еще море всяких других классов. Важен просто контракт — «я умею слушать кнопку». Это позволяет в разы повысить гибкость при проектировании. Возвращаясь к методу — он принимает в качестве параметра специальный класс/объект, который содержит интересную информацию об источнике события — в данном случае о кнопке. В нашем случае нам очень интересен параметр, который мы устанавливали — getActionCommand/setActionCommand. Именно он нам скажет какая кнопка нажата. Еще раз обратите внимание на приятную возможность слушать события от обеих кнопок.
Ну и наконец код для запуска нашей формы:

Предлагаю вам расширить пример — добавить туда кнопки LEFT и RIGHT и двигать квадрат в стороны. Также вы можете сделать проверку, чтобы квадрат «не убежал» за пределы экрана.
Исходный код примера находится здесь — MoveSquare

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

И теперь нас ждет следующая статья: Расширенное описание классов.

34 comments to Интерфейсы

  • javaNoob  says:

    Так он и должен работать… но если Вы захотите Чтобы передвижениями робота внезапно заинтересовался какой то другой клас, (ну например решили вы добавить на поле к роботам, наблюдательную вышку(class outPost), (который заметьте,не имеет ничего общего с роботами(но хочет за ними наблюдать), то у Вас ничего не выйдет, потому что Робот после изменения в нем Вами типа данных с RobotListener на SimpleRobotListener, и удаления интерфейса как такового потерял способность общаться с внешним миром, кроме разумеется экземпляров класса SimpleRobotListener, и его потомков…

  • Grif  says:

    Вообще здорово и сама идея интерфейса и понятие слушатель. Т.е. я так понял это можно сравнить со стуком в дверь, в данном случае дверь выполняет роль интерфейса — объект стучит в дверь а по ту сторону двери кто-то слышит стук и реагирует на него, при этом ни объект стука ни слушатель ничего друг о друге могут не знать. Т.е. интерфейс кроме всего прочего реализует событие для слушателей. Единственно не очень понятно зачем объект стука должен регистрировать слушателей …. наверно это особенности внутренней реализации или какая-то разновидность системы безопасности …

    • admin  says:

      Объект стука должен занть в какую дверь стучать — для этого регистрация у кнопки и есть.

  • Grif  says:

    Пример с дверью не очень удачный. Скорее больше подойдет пример с автомобилем:
    Когда автомобиль заводится слушатели слышат включение двигателя, в данном случае интерфейсом выступает воздух, с одной стороны он соприкасается с машиной, с другой со слушателями и слушатели понимают, что этот звук связан именно с этой машиной (нам как раз в программировании и нужно понимать какое событие с каким объектом связанно). …. Наверно как-то так ….

    • admin  says:

      Тоже неплохая аналогия.

  • Grif  says:

    Слушатели фактически являются реализаторами событий, другими словами методов, которые «зарезервированы» в интерфейсах. Т.е. интерфейсы запускают свои методы, которые реализованы в слушателях, поэтому и требуется жёсткое их повторение в классах реализующих интерфейсы. Другое дело, что сама реализация этих методов(событий) может зависеть исключительно от фантазии программиста 🙂 ну и конечно ограничений в рамках которых он работает. 🙂

  • Grif  says:

    Здоровская статья. Просто зачётная … вот только домашнее задание слабенькое. Честно говоря много новых слов, однако после того как слова и их значения укладываются в памяти, то их написание теряет смысл т.к. общая картина и так видна … добавить две кнопки прослушать их сделать перемещение по Х и сделать чтоб Х и У не выходили за рамки заданных координат … слишком просто. 🙂 Если бы мог, я бы потребовал чего-то более сложного 🙂 (простите меня ради бога за наглость)

    • admin  says:

      Я подумаю 🙂

      • Fidel  says:

        Grif says:
        Здоровская статья. Просто зачётная … вот только домашнее задание слабенькое.

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

        • Grif  says:

          При изменениях размеров окна тоже особо не усложняется, делаем проверку на текущие координаты, запрещаем изменение размеров если изменённые размеры не вмещают в себя габариты объекта+координаты (это для окна), а для самого объекта перед каждым передвижением делаем проверку на размеры окна … и всего делов — то 🙂

  • Булат  says:

    Как я понял понятие «интерфейс» необходимо, чтобы передать информацию о действиях объекта во вне. Но почему это нельзя реализовать просто через переменную? Почему в момент когда робот совершил какое-то действие нам не записать этот факт в переменную и потом ее вывести или передать кому надо. Зачем для этого вводится специальное понятие «Интерефейс»? Что до мене не дошла идея 🙂 , простите.

    • admin  says:

      Предположим, что мы записали факт в какую-то переменную. Теперь внимание вопрос — КАК можно будет передать эту переменную какому-то объекту ? Какими методами/свойствами должен обладать этот объект ? Что мы, как передающая сторона, должны знать об этом объекте ?

  • Булат  says:

    Ну, например, мы запишем эту информацию в файл, а потом объект, которому это нужно считает ее от туда. Грубо говоря, пусть объект ведет лог. И еще: а зачем тогда понятие видимости переменной public?
    Простите если вопросы дилетантские )
    Просто я понял, что если вопросы задаешь, то быстрее начинаешь понимать.

    • admin  says:

      Для того, чтобы передать сообщение об изменении своего состояния писать файл ?
      Если я хочу сообщить в рассылке о появлении нового товара в своем магазине, мне достаточно иметь ТОЛЬКО e-mail. И больше мне НИЧЕГО не надо. Это и есть идея интерфейса — вы заранее определяете некоторые правила, которые позволят вам сообщать ЛЮБОМУ объекту о чем-нибудь. И для этого не надо, чтобы объект был наследником определенного класса — он просто должен реализовать интерфейс.
      Или другая аналогия — профессия. Вам нужен сантехник и вас не волнует, сколько ему лет, какой он национальности — вам важен факт определенного профессионального навыка.

      По поводу public — переменная может быть неизменяемая — тогда она может быть public. Да и это в общем дело разработчика — его выбор. Может ему это удобнее в какой-то ситуации.

  • Булат  says:

    Кажется стало понятнее.
    Еще вопрос почему окно, которое мы формируем в проекте MoveSquare имеет размер меньше, чем мы указываем в
    setBounds(100, 100, 400, 400);
    400 — это же ширина.
    А меня получается по вертикали 310 а по горизонтали меньше. Я это определил, когда ставил ограничения на выход квадрата за рамки окна.

    • admin  says:

      Надо смотреть код — я так не отвечу. Точно могу сказать, что высота окна и высота компонента это совсем разные вещи. Высота окна учитывает вместе с заголовком, а компонент на форме уже меньше.

  • silent  says:

    Добрый день!
    Подскажите пожалуйста, почему в последнем примере реализация интерфейса ActionListener выполнена в том же классе, что и наследование и отрисовка компонента (SquareComponent)
    public class SquareComponent extends JComponent implements ActionListener

    Возможно, было бы более логично вынести реализацию интерфейса в отдельный класс (например, SquareComponentListener) — как в первом примере?
    Иначе получается смешение логики выполняемых операций. Один класс и реализует визуальную компоненту и является слушателем одновременно.

    • admin  says:

      Это уже очень субъективно. В принципе можно было и отдельным классом. Но тогда отдельный класс должен знать о компоненте, чтобы посылать ему команды.
      Наверно можно было бы придумать еще более заковыристую конструкцию. Я в этом случае решил не мудрить.

  • Nibbler  says:

    Проверку выхода за границы реализовал в виде отдельного метода в классе SquareComponent:

    Чтобы квадрат не «проваливался» на величину STEP за пределы окна, сначала делаем приращение, а потом — проверку с откатом назад, если она не прошла:

    Четыре кнопки по краям заняли все границы поля. Ничего не могу с собой поделать — все время хочется что-нибудь «облагородить» на свой вкус 🙂 Решил уменьшить размеры кнопок — не получилось. Позже вычитал, что это — особенность BorderLayout-а — растягивать кнопки на всю ширину/высоту. Появилась другая бредовая идея, которая была после некоторых мучений реализована. Квадрат был размещен в отдельном фрейме — SquareFrame, созданном по образу и подобию того, что мы делали в самом первом примере с овалом. А кнопки остались в прежнем окне. Вот как получилось из одного окна управлять элементом другого окна:

    Вообще, тема мне показалась достаточно важной и достаточно сложной. Чтобы лучше запомнить, разложил ее как мог в виде пунктов-тезисов:
    1. Описываем интерфейс (контракт)
    2. Описываем класс слушателя, в котором реализуем методы интерфейса
    3. Создаем экземпляр слушателя
    4. Регистрируем созданного слушателя в «передатчике» как переменную класса интерфейс (ссылку на интерфейс)
    5. После этого интерфейсом можно пользоваться: Передатчик может вызывать методы интерфейса.
    Пока, конечно, сложно все уложить в мозгу. Хочу попробовать сделать, чтобы панель управлялась не только нашими кнопками, но и с клавиатуры — кнопками вверх-вниз-влево-вправо.

  • Nibbler  says:

    Знаки больше/меньше потерялись в коде. Наверное, нужно было добавлять их как > < — поздно заметил.

  • Firefly  says:

    Скажите пожалуйста, как отслеживать изменение размера формы? Каким методом считывать ширину и высоту компоненты (чтобы квадрат «не убежал» за пределы экрана)?

  • Сергей  says:

    Упростил первый пример: удалил файл интерфейса RobotListener.java, в SimpleRobotListener.java закоментировал две фразы «@Override» и фразу «implements RobotListener», в файле Robot.java в объявлении переменной и в описании метода заменил RobotListener на SimpleRobotListener, и все отработало также!!! Так в чем же необходимость интерфейса ?

    • admin  says:

      Понимание интерфейса — очень непростая задача. Наверно лучше всего представить это на примере набора персонала в компанию.
      Вы же набираете не конкретные личности (Васю, Сашу, Таню, Лену), а создаете некоторую структуру организации с нужными специальностями и под них уже набираете специалистов. Прграмы часто строятся так же — вы сначала определяете какие «специалисты» нужны данному объекту для его работы, а потом под них используете либо готовую реализацию, либо создаете свою. Но это крайне удобно — заранее не думать о реализации, просто сказать «хочу такого специалиста»

  • Oleh  says:

    Попробую и я
    Допустим есть класс «Кот» и класс «Собака»
    экземпляры этих классов могут издавать звуки, но звуки у котов и собак разные, одни мяукают другие лают, а нам к примеру нужно написать программу котоая будет издавать звуки в зависимости от Кот это или Собака (а может и весь зоопарк), но каждый должен издавать свой определенный звук, при этом в программе которая это реализует с помощью интерфейса можно применить только один метод, а на выходе получить разные звуки,в зависимости от экземпляра класса.
    1. Создаем интерфейс «Крик» и в нем прописываем сигнатуру метода «Издать звук»
    2. Создаем классы «Кот», «Собака», «Хрюшка»….которые реализуют интерфейс «Крик» и в них прописываемым методы с реализацией(реализация для каждого класса своя, т.к. звуки у разных животных разные).
    3. В исполняющем классе (main) все єкземпляры животных можно подвести к одному знаменателю — интерфейсной переменной (Крик е = new Собака и т.д.) Ну а потом применить к ним один метод «Издать звук», но на выходе получим разную реализацию — лай, хрюканье, карканье….
    Похожий пример можно привести с фигурами и методом интерфейса, который бы вычислял бы их площадь и называл бы название фигуры….Или можно было бы сделать такой интерфейсный метод , который бы позволял потом сравнивать между собой разные геометрические фигуры (то есть раные екземпляры классов), например по площади…

  • Oleh  says:

    добавлю еще один пример…
    есть классы которые говорят так, если ваш класс реализует определенный интерфейс, а значит в нем переопределен метод интерфейса, то этот класс (не ваш) с помощью своего метода может определенным образом манипулировать экземпляром вашего класса, например, если ваш класс реализует интерфейс Comparable, то метод sort ()из класса Array может сортировать определенным образом массив экземпляров вашего класса.

  • Oleh  says:

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

    • admin  says:

      На мой взгляд не самая удачная аналогия получилась. Возможно более правильным будет так: любой замок может реализовать интерфейс «Смена состояния», который включает две функции «открыть» и «закрыть». Любой замок по идее должен уметь это делать — открыться и закрыться. А вот как он это делает — уже зависит от реализации замка.

  • Miko_style  says:

    То есть получается, интерфейсы — это некий тип действий, который по вашему предположению может будет выполняться объектами разных классов? Ну, например, я планирую написание торговой программы, где торговать можно будет как «руками» пользователю, так и в автоматическом режиме. И я создаю интерфейс «продажа» который будет принимать параметры (цена , количество), но продажа в моем случае это не стандартная процедура, например при автопродаже мне нужно еще вести лог. Само собой в процессе написания бизнес-логики я буду создавать два отдельных класса: для мануальной торговли и для торговли автоматической, и в каждом из них я просто реализую интерфейс «продажа», но при исполнении «продажи» для автоматической торговли в, теле метода я так же добавлю нужное логирование.

    В придуманном мной примере рационально применение интерфейсов, как вы считаете?

    • admin  says:

      В принципе так можно думать. Правла пример не совсем удачный (на мой взгляд) — т.к. это две разные системы.
      Интерфейс интересене, когда есть взаимодесвтие двух и более систем. Когда одна система знает ЧТО другая умеет делать, но не знает КАК.
      Мне нравится аналогия интерфейсов с профессиями — вы приглашаете специалиста определенной специальности, но вы не знаете, как выглядит этот человек, как и каким инструментом он выполняет свою работу. Но что здорово — вы можете приглашать РАЗНЫХ специалистов для выполнения ОДИНАКОВОЙ работы.

  • E=MC^2  says:

    Решил написать змейку,но возникла проблема из KeyListener,при компиляции нажатие кнопок считуется «через раз»,то есть при запуске змейка может то двигаться то нет (прямо если повезет или нет).При чем пишет вот такой коментарий в конструкторе Snake: Вызов переопределяемых методов, может быть привести к сбою, поскольку на момент вызова переопределенного метода инициализирован не полностью.Можете объяснить в чем может быть проблема?И да,спасибо за урок. Вот код:
    package Snakeobjects;

    import java.awt.Color;
    import static java.awt.Color.BLACK;
    import static java.awt.Color.darkGray;
    import static java.awt.Color.white;
    import java.awt.Graphics;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.awt.event.KeyAdapter;
    import java.awt.event.KeyEvent;
    import static java.awt.event.KeyEvent.VK_DOWN;
    import static java.awt.event.KeyEvent.VK_LEFT;
    import static java.awt.event.KeyEvent.VK_RIGHT;
    import static java.awt.event.KeyEvent.VK_UP;
    import javax.swing.JFrame;
    import static javax.swing.JFrame.EXIT_ON_CLOSE;
    import javax.swing.JPanel;
    import javax.swing.Timer;

    /*
    * To change this license header, choose License Headers in Project Properties.
    * To change this template file, choose Tools | Templates
    * and open the template in the editor.
    */

    /**
    *
    * @author User
    */
    public class Snake extends JPanel implements ActionListener{

    public static JFrame jframe;
    public static final int SCALE = 24;
    public static final int WIDTH1 = 30;
    public static final int HEIGHT1 = 30;
    public static final int STARTX = 100;
    public static final int STARTY = 0;
    int SPEED =20;

    Snake1 s = new Snake1(5,6,5,5);
    Timer timer = new Timer(1000/SPEED, this);

    public Snake(){
    timer.start();
    addKeyListener(new KeyBoard());
    setFocusable(true);

    }

    @Override
    public void paint(Graphics g){
    g.setColor(white);
    g.fill3DRect(0, 0, WIDTH1*SCALE, HEIGHT1*SCALE, true);
    for(int x=0;x<SCALE*WIDTH1 ; x+=SCALE){
    g.setColor(BLACK);
    g.drawLine(x,0,x,SCALE*HEIGHT1);
    }
    for(int y=0;y<SCALE*HEIGHT1 ; y+=SCALE){
    g.setColor(BLACK);
    g.drawLine(0,y,SCALE*WIDTH1,y);
    }
    for(int k =0;kjframe.getBounds().width){
    SX[0]=0;
    }
    if(SY[0]*SCALE>jframe.getBounds().height){
    SY[0]=0;
    }
    if(SY[0]*SCALE<jframe.getBounds().y){
    SY[0]=jframe.getBounds().height/SCALE;
    }
    if(SX[0]*SCALE0;l—){
    SX[l]=SX[l-1];
    SY[l]=SY[l-1];
    }
    if(direction == 0){
    SY[0]—;

    }
    if(direction == 1){
    SX[0]++;

    }
    if(direction == 2){
    SY[0]++;

    }
    if(direction == 3){
    SX[0]—;

    }

    }

    }

  • E=MC^2  says:

    Ем,что-то не так :
    public Snake(){
    timer.start();
    //Здесь коментарий addKeyListener(new KeyBoard());
    setFocusable(true);

    }
    public class KeyBoard extends KeyAdapter{
    @Override
    public void keyPressed(KeyEvent event){
    int key = event.getKeyCode();
    if(key==VK_UP && s.direction !=2){
    s.direction=0;
    }
    if(key==VK_RIGHT && s.direction !=3){
    s.direction=1;

    }
    if(key==VK_DOWN && s.direction !=0){
    s.direction=2;

    }
    if(key==VK_LEFT && s.direction !=1){
    s.direction=3;
    }
    }
    Вот скинул только собственно главное

  • DimaSoul  says:

    Можно вызывать поле класса без создания его экземпляра ? Просто если нет , тогда не понятно , как устанавливается связь между полями UP и DOWN (класса MoveSquareFrame) и классом SquareComponent .Спасибо

    • admin  says:

      Если поле объявлено как static — тогда можно.

Leave a reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">

Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.