}
// Подкласс:
class В extends А{
// Определение наследуемого абстрактного метода:
void саllme(){
Sуstеm. оut. рrintln("Первый метод");}
}
сlass AbstDemo{
puplic static void main(String args[]){
// Объект подкласса:
В obj=new В();
obj. callme();
obj. callmetoo();}
}
Пример достаточно простой: описывается абстрактный суперкласс А, на основе которого затем создается подкласс В. Суперкласс А содержит абстрактный метод call() и обычный (неабстрактный) метод callmetoo(). Оба метод наследуются в классе В. Но поскольку метод саll() абстрактный, то он описан в классе В.
Методом саll() выводится сообщение Первый метод, а методом саllmetoo() - сообщение Второй метод. В главном методе программы создается объект подкласса и последовательно вызываются оба метода. В результате получаем сообщения:
Первый метод
Второй метод
Что касается практического использования абстрактных классов, то обычно они бывают полезны при создании сложных иерархий классов. В этом случае абстрактный класс, находящийся в вершине иерархии, служит своеобразным шаблоном, определяющим, что должно быть в подклассах. Конкретная же реализация методов выносится в подклассы. Такой подход, кроме прочего, нередко позволяет избежать ошибок, поскольку будь у суперкласса только неабстрактные наследуемые методы, было бы сложнее отслеживать процесс их переопределения в суперклассах. В то же время, если не определить в подклассе абстрактный метод, при компиляции появится ошибка.
Отметим еще одно немаловажное обстоятельство, которое касается наследования вообще. В некоторых случаях необходимо защитить метод от возможного переопределения в подклассе. Для этого при описании метода в его сигнатуре указывается ключевое слово finаl. Если. это ключевое слово включить в сигнатуру класса, этот класс будет защищен от наследования - на его основе нельзя будет создать подкласс. Третий способ использования ключевого слова final касается описания полей (переменных). В этом случае оно означает запрет на изменение значения поля, то есть фактически означает определение константы.
Примеры программ
Рассмотрим некоторые примеры, в которых имеет место наследование классов и переопределение методов.
Комплексная экспонента
Далее в листинге 12 приведен код программы, .в которой создается суперкласс для реализации комплексных чисел и выполнения базовых операций с ними: сложения комплексных чисел, умножения комплексных чисел и произведения комплексного и действительного чисел. На основе суперкласса создается подкласс, в котором описан метод для вычисления экспоненты от комплексного аргумента.
Листинг 12. Вычисление комплексной экспоненты
// Суперкласс:
c1ass Соmрl{
// Действительная и мнимая части числа:
double Re, Im;
// Метод для вычисления суммы комплексных чисел:
Соmрl sum(Compl obj){
Соmрl tmp=new Соmрl();
tmp. Re=Re+obj. Re;
tmp. Im=Im+obj. Im;
return tmp;}
// Mетод для вычисления произведения комплексных чисел:
Соmрl prod(Compl obj){
Соmрl tmp=new Соmрl();
tmp. Re=Re*obj. Re-Im*оbj. Im;
tmp. Im=Im*obj. Re+Re*obj. Im;
return tmp; }
// Метод перегружен для вычисления произведения
// комплексного и действительного чисел:
Compl prod(double х){
Compl tmp=new Compl();
tmp. Re=Re*x;
tmp. Im=Im*x;
return tmp; }
// Метод для отображения полей объекта:
void show(){
Sуstеm. оut. рrintln("Действительная часть Re="+Re);
System. out. рrintln( "Мнимая часть Im="+ Im); }
// Конструктор без аргумента:
Соmрl() { Re=0;
Im=0; }
// Конструктор с одним аргументом:
Compl(double х){
Re=x;
Im=0; }
// Конструктор с двумя аргументами:
Compl(double x, double у){
Re=x;
Im=y; }
// Конструктор создания копии:
Compl(Compl obj){
Re=obj. Re;
Im=obj. Im; }
}
// Подкласс:
class ComplNums extends Compl{
// Количество слагаемых ряда:
private int n;
// Метод для вычисления комплексной экспоненты:
ComplNums СЕхр(){
// Начальное значение - объект суперкласса:
Compl tmp=new Compl(1):
// Начальная добавка - объект суперкласса:
Compl q=new Compl(this):
// Индексная переменная:
int i;
// Вычисление ряда:
for(i=1; i<=n; i++) {
tmp=m(q);
q=q. prod(this).prod(1.0/(i+1)); }
// Результат - объект подкласса:
return new ComplNums(tmp);}
//Конструктор суперкласса без аргументов:
ComplNums(){
Super();
n=100;}
// Конструктор суперкласса с одним аргументом:
ComplNums(double х){
super(x);
n=100;}
// Конструктор суперкласса с двумя аргументами:
ComplNums(double x, double у){
super(x,.у);
n=100;}
// Конструктор суперкласса с тремя аргументами:
ComplNums(double x, double y, int m){
super(x, у);
n=m; }
// Конструктор создания объекта подкласса
// на основе объекта суперкласса:
ComplNums(Compl obj){
super(obj);
n=100;}
// Конструктор создания копии для суперкласса:
СоmрlNums (СоmрlNums obj) {
super(obj);
n=obj. n;}
}
class ComplExtendsDemo{
public static void main(String[] args){
ComplNums z=new ComplNums(2, 3);
// Вычисление комплексной экспоненты:
z. CExp().show();}
}
В суперклассе Соmрl описано два поля Re и Im - оба типа doublе. Кроме этого, класс имеет метод sum() для вычисления суммы двух комплексных чисел, реализованных в виде объектов класса Соmрl. В классе также есть перегруженный метод prod() для вычисления произведения двух комплексных чисел, а также комплексного числа на действительное число. Конструкторы класса Соmрl позволяют создавать объекты без передачи аргументов, а также с передачей одного и двух аргументов, кроме того, у класса имеется конструктор копирования. В последнем случае конструктору в качестве аргумента передается объект того же класса - на основе этого объекта создается копия. На основе суперкласса Соmрl создается подкласс СоmрlNums. Кроме наследуемых из суперкласса полей и методов, в подклассе описывается закрытое целочисленное поле n, которое определяет количество слагаемых при вычислении ряда для экспоненты. Если через z обозначить комплексное число, которое передается аргументом экспоненте, то результат вычисляется в виде:

В подклассе предусмотрены конструкторы создания объектов с передачей конструктору до трех аргументов. Также описан конструктор копирования - в этом случае объект подкласса создается на основе другого объекта подкласса. Кроме того, имеется конструктор создания объекта подкласса на основе объекта суперкласса.
Комплексная экспонента вычисляется методом СЕхр(). Аргументом экспоненты является комплексное число, реализованное через объект вызова. Результатом является объект подкласса. В самом методе командой Соmрl tmp=new Соmрl(1) создается локальный объект tmp суперкласса с начальным единичным значением. В этот объект будет записываться сумма комплексного ряда. Начальное значение для добавки при вычислении суммы определяется локальным объектом суперкласса q. Этот объект создается командой Compl q=new Compl(this). Начальное значение добавки - это комплексный аргумент экспоненты. При создании объекта вызывается конструктор копирования, относящийся к суперклассу. При этом аргументом указана ссылка на объект вызова, то есть на объект подкласса. Однако благодаря тому, что объектная переменная суперкласса может ссылаться на объект подкласса, такая ситуация корректна..
Вычисление результата осуществляется в цикле. В теле цикла две команды. Первой командой tmp=m(q) выполняется прибавление к текущему значению суммы очередной добавки. Второй командой q=q. prod(this).prod(1.0/(i·+1)) изменяется сама добавка (добавка умножается на аргумент экспоненты и затем делится на значение индексной переменной i, увеличенное на единицу). Обращаем внимание читателя на использование в данном случае ссылки this.
После завершения цикла командой new ComplNums(tmp) на основе локального объекта суперкласса создается анонимный объект подкласса, который и возвращается в качестве результата методом.
После выполнения программы получаем следующий результат:
Действительная часть Re=-7.315110094901102
Мнимая часть Im=1.042743656235904
Отметим, что как в суперклассе, так и в подклассе описана лишь незначительная часть методов, требующихся при работе с комплексными числами. На практике таких методов приходится описывать намного больше. Кроме того, не все конструкторы использованы при вычислении результата - комплексной экспоненты. Код для этих конструкторов приведен в качестве иллюстрации.
Произведение полиномов и ряд Тейлора
В следующей программе реализована процедура вычисления произведения полиномов и вычисления ряда Тейлора для произведения двух функций (ряды Тейлора для каждой из которых известны). При этом применяется механизм наследования. Программный код приведен в листинге 13. Сразу отметим, что структура программы и, в частности, организация классов далеко не оптимальны - пример иллюстративный и позволяет лучше понять некоторые особенности механизма наследования.
Листинг 13. Произведение полиномов и ряд Тейлора
// Суперкласс:
class PolyBase{
// Коэффициенты полинома:
double[] а;
// Метод для вычисления значения полинома в точке:
double value(double х){
doublе s=0, q=1;
for (int i =0; i<а. length; i ++) {
s+=a[i]*q;
q*=x; }
return s;}
// Степень полинома:
int power() {
for (int i =а. length-1; i >0; i-- ) {
if(a[i]!=0) return i;}
return 0;}
// Отображение коэффициентов и степени полинома:
void show() {
Sуstеm. оut. рrintln("Коэффициенты полинома:");
for(int i=0;i<a. length;i++)
System. out. print(a[i]+" ");
System. out. print("\nСтепень полинома: ");
System. out. println(power()+" . \n"); }
}
// Подкласс:
class PolyDerive extends PolyBase{
// Метод для вычисления произведения полиномов:
PolyBase prod(PolyBase Q){
int i, j, n;
n=power()+Q. power()+1;
PolyBase tmp=new PolyBase();
tmp. a=new double[n];
for(i=0;i<=power();i++){
for(j=0;j<=Q. power();j++){
tmp. a[i+j]+=a[i]*Q. a[j];}
}
return tmp;}
// Отображение параметров полинома и значения в точке:
|
Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 |


