Java для школьников. Занятие №12. Множественное наследование с помощью интерфейсов. Расчет траектории движения тела, брошенного под углом к горизонту

Предыдущее занятие Следущее занятие

После ознакомления с основными принципами наследования на прошлом занятии нетрудно представить себе ситуацию, когда программист захочет использовать код от нескольких классов. НО! В языке программирования (ЯП) Java возможности наследования от нескольких классов нет. Для использования множественного наследования в Java существует специальная конструкция - интерфейс.

В отличие от других ЯП, где наследование от нескольких классов практикуется (например C++) использование интерфейсов избавляет от некоторых проблемных вариантов наследования см. - https://ru.wikipedia.org/wiki/%D0%A0%D0%BE%D0%BC%D0%B1%D0%BE%D0%B2%D0%B8... , http://www.javaportal.ru/java/articles/mnj.html
хотя, как и многое в информационных технологиях, использование интерфейсов имеет и свои недостатки.

Однако, углубляться в тонкости использования различных вариантов наследования на нашем этапе изучения ООП и Java пока явно рано. Поэтому просто давайте познакомимся с применением интерфейсов на примере, заимствованном из учебника Информатики профильного уровня 11 класса Н.Угриновича [1]. Правда нам придется переработать этот пример применительно к ЯП Java и к изучаемой теме.

Листинг 1. Файл Shooter.java - расчет траектории движения тела, брошенного под углом к горизонту (система "Стрелок-наводчик") с помощью интерфейсов Java

import java.io.*;
import java.math.*;

interface Angle {
        void setAngle(double a);
}

interface Power {
        void setVelocity(double v);
}

interface Target {
        public void setTargetDistance(double s);
        void setTargetHeight(double h);
}

public class Shooter implements Angle, Power, Target {

        double  a, v0, s, h;
        public void setAngle(double a) {this.a = a;}
        public void setVelocity(double v) {this.v0 = v;}
        public void setTargetDistance(double s) {this.s = s;}
        public void setTargetHeight(double h) {this.h = h;}
       
        public static void main (String[] args) {
                Shooter sh = new Shooter();
                sh.setAngle(40.0);
                sh.setVelocity(20.0);
                sh.setTargetDistance(40.0);
                sh.setTargetHeight(1.0);
                double Y = sh.s * Math.tan( sh.a * Math.PI/180 ) - ( 9.8 * sh.s * sh.s ) / ( 2 * sh.v0 * sh.v0 * Math.cos( sh.a * Math.PI/180 ) * Math.cos( sh.a * Math.PI/180 ) );
                System.out.println("Наводчик: " + Y);
                if (Y < 0) System.out.println("Недолет");
                if (Y > 0 && Y <= sh.h) System.out.println("Попал!");
                if (Y > sh.h) System.out.println("Перелет");
        }
}

Давайте разберем внимательно эту (уже достаточно сложную) программу. В ней определено сразу 3 интерфейса: Angle, Power и Target.

Определение (описание) интерфейса схоже с определением класса. Для этого просто применяется другое ключевое слово interface вместо class. Еще одно правило - все методы интерфейса при определении должны обязательно быть пустыми, как, например, в интерфейсе Angle (угол). В нем определен метод установки угла бросания setAngle(), который должен обязательно реализовать класс, наследующий этот единственный метод от данного интерфейса.

Другими словами интерфейс - это заготовка (набросок) для класса, который будет его использовать. Причем, код может быть заимствован от нескольких интерфейсов одновременно и для указания наследования от интерфейса к определению класса добавляется ключевое слово implements, которое можно перевести как "реализует". Это и использовано при определении нашего класса Shooter (стрелок) в котором приведена реализация методов, заимствованных у интерфейсов Angle, Power и Target с использованием модификатора public, а также описаны переменные класса - a, v0, s, h, хранящие в памяти компьютера угол, начальную скорость, расстояние до мишени и ее высоту, соответственно.

Далее, функция main() создает экземпляр sh класса Shooter, задает угол обстрела, начальную скорость, расстояние до мишени и ее высоту с помощью соответствующих методов этого класса и рассчитывает координату Y (высоту) снаряда/камня в точке с координатой X равной расстоянию до мишени s. Далее в работу включается "наводчик", который сравнивает, используя оператор сравнения if (см. оператор сравнения в Занятии 9), полученное значение Y с заданной высотой мишени и выдает сообщения о поражении цели (Недолет, Перелет, Попал!).

Кстати, понять физику процесса движения тела, брошенного под углом к горизонту, поможет отличная онлайн иллюстрация данной темы: http://somit.ru/roliki/new1.htm

Итак, свод правил, исполнение которых при использовании наследования от интерфейсов обязательно:
* все методы интерфейса при его определении должны обязательно быть пустыми;
* методы, определенные в интерфейсе, в классе должны быть обязательно реализованы, да еще с использованием модификатора public;
* для указания наследования от интерфейса к определению класса добавляется ключевое слово implements.

На полях:
* как откомпилировать программу на Java - см. Занятие 3
* попробуйте изменить параметры "броска" - скорость, угол, расстояние и высоту мишени, добиваясь, однако, попадания в цель. После каждого изменения исходного кода программу нужно перекомпилировать
* теперь попробуйте доработать программу и вводить нужные параметры с помощью аргументов командной строки используя материалы Занятия 10

Литература
1. Н.Д.Угринович Информатика и ИКТ. Профильный уровень: Учебник для 11 класса - 2 изд. исп. и доп. - М.: БИНОМ. Лаборатория знаний, 2009 с.15 ISBN 978-5-9963-0047-1
2.Герберт Шилдт "Полный справочник по Java" 7-е издание, Издательский дом "Вильямс", 2007г., ISBN 978-5-8459-1168-1, с.225